diff --git a/docs/src/guide/index.md b/docs/src/guide/index.md index 4e8684fc32..369ad05144 100644 --- a/docs/src/guide/index.md +++ b/docs/src/guide/index.md @@ -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. diff --git a/platform/commonUI/edit/src/creation/AddActionProvider.js b/platform/commonUI/edit/src/creation/AddActionProvider.js index 582d7627d8..08b512ca55 100644 --- a/platform/commonUI/edit/src/creation/AddActionProvider.js +++ b/platform/commonUI/edit/src/creation/AddActionProvider.js @@ -54,8 +54,7 @@ define( AddActionProvider.prototype.getActions = function (actionContext) { var context = actionContext || {}, key = context.key, - destination = context.domainObject, - self = this; + destination = context.domainObject; // We only provide Add actions, and we need a // domain object to serve as the container for the @@ -66,18 +65,16 @@ 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) { + return ['timeline', 'activity'].map(function (type) { return new AddAction( - type, + this.typeService.getType(type), destination, context, - self.$q, - self.dialogService, - self.policyService + this.$q, + this.dialogService, + this.policyService ); - }); + }, this); }; return AddActionProvider; diff --git a/platform/commonUI/edit/src/creation/CreateWizard.js b/platform/commonUI/edit/src/creation/CreateWizard.js index c2383743b5..79416ac4b4 100644 --- a/platform/commonUI/edit/src/creation/CreateWizard.js +++ b/platform/commonUI/edit/src/creation/CreateWizard.js @@ -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 ); } diff --git a/platform/commonUI/edit/test/creation/AddActionProviderSpec.js b/platform/commonUI/edit/test/creation/AddActionProviderSpec.js index 560b2bd0f7..57493562e5 100644 --- a/platform/commonUI/edit/test/creation/AddActionProviderSpec.js +++ b/platform/commonUI/edit/test/creation/AddActionProviderSpec.js @@ -31,9 +31,7 @@ define( var mockTypeService, mockDialogService, mockPolicyService, - mockCreationPolicy, - mockCompositionPolicy, - mockPolicyMap = {}, + mockTypeMap, mockTypes, mockDomainObject, mockQ, @@ -55,49 +53,33 @@ define( ); mockType.hasFeature.andReturn(true); mockType.getName.andReturn(name); + mockType.getKey.andReturn(name); return mockType; } beforeEach(function () { mockTypeService = jasmine.createSpyObj( "typeService", - ["listTypes"] - ); - mockDialogService = jasmine.createSpyObj( - "dialogService", - ["getUserInput"] - ); - mockPolicyService = jasmine.createSpyObj( - "policyService", - ["allow"] + ["getType"] ); + mockDialogService = {}; + mockPolicyService = {}; + mockDomainObject = {}; - mockDomainObject = jasmine.createSpyObj( - "domainObject", - ["getCapability"] - ); - - //Mocking getCapability because AddActionProvider uses the - // type capability of the destination object. - mockDomainObject.getCapability.andReturn({}); - - mockTypes = ["A", "B", "C"].map(createMockType); + mockTypes = [ + "timeline", + "activity", + "other" + ].map(createMockType); + mockTypeMap = {}; mockTypes.forEach(function (type) { - mockPolicyMap[type.getName()] = true; + mockTypeMap[type.getKey()] = type; }); - mockCreationPolicy = function (type) { - return mockPolicyMap[type.getName()]; - }; - - mockCompositionPolicy = function () { - return true; - }; - - mockPolicyService.allow.andReturn(true); - - mockTypeService.listTypes.andReturn(mockTypes); + mockTypeService.getType.andCallFake(function (key) { + return mockTypeMap[key]; + }); provider = new AddActionProvider( mockQ, @@ -107,29 +89,16 @@ define( ); }); - it("checks for creatability", function () { - provider.getActions({ + it("provides actions for timeline and activity", function () { + var actions = provider.getActions({ key: "add", 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 - 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'); }); }); } diff --git a/platform/commonUI/edit/test/creation/CreateWizardSpec.js b/platform/commonUI/edit/test/creation/CreateWizardSpec.js index a2c96db063..3973afc945 100644 --- a/platform/commonUI/edit/test/creation/CreateWizardSpec.js +++ b/platform/commonUI/edit/test/creation/CreateWizardSpec.js @@ -175,7 +175,7 @@ define( expect(mockPolicyService.allow).toHaveBeenCalledWith( 'composition', mockOtherType, - mockType + mockDomainObject ); }); diff --git a/platform/containment/bundle.js b/platform/containment/bundle.js index d77697e8dc..a64710b974 100644 --- a/platform/containment/bundle.js +++ b/platform/containment/bundle.js @@ -40,9 +40,6 @@ define([ { "category": "composition", "implementation": CompositionPolicy, - "depends": [ - "$injector" - ], "message": "Objects of this type cannot contain objects of that type." }, { diff --git a/platform/containment/src/CapabilityTable.js b/platform/containment/src/CapabilityTable.js deleted file mode 100644 index 917b3bd48d..0000000000 --- a/platform/containment/src/CapabilityTable.js +++ /dev/null @@ -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; - } -); diff --git a/platform/containment/src/ComposeActionPolicy.js b/platform/containment/src/ComposeActionPolicy.js index a1e36fd024..0a107756af 100644 --- a/platform/containment/src/ComposeActionPolicy.js +++ b/platform/containment/src/ComposeActionPolicy.js @@ -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 ); }; diff --git a/platform/containment/src/CompositionPolicy.js b/platform/containment/src/CompositionPolicy.js index 1e69fa64cb..60d0008fb0 100644 --- a/platform/containment/src/CompositionPolicy.js +++ b/platform/containment/src/CompositionPolicy.js @@ -26,8 +26,8 @@ * @namespace platform/containment */ define( - ['./ContainmentTable'], - function (ContainmentTable) { + [], + function () { /** * Defines composition policy as driven by type metadata. @@ -35,21 +35,33 @@ define( * @memberof platform/containment * @implements {Policy.} */ - 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 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; diff --git a/platform/containment/src/ContainmentTable.js b/platform/containment/src/ContainmentTable.js deleted file mode 100644 index 17bc69db2d..0000000000 --- a/platform/containment/src/ContainmentTable.js +++ /dev/null @@ -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; - } -); diff --git a/platform/containment/test/CapabilityTableSpec.js b/platform/containment/test/CapabilityTableSpec.js deleted file mode 100644 index 646ef82882..0000000000 --- a/platform/containment/test/CapabilityTableSpec.js +++ /dev/null @@ -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(); - }); - - }); - } -); diff --git a/platform/containment/test/ComposeActionPolicySpec.js b/platform/containment/test/ComposeActionPolicySpec.js index e7e977c781..b1b0aaff80 100644 --- a/platform/containment/test/ComposeActionPolicySpec.js +++ b/platform/containment/test/ComposeActionPolicySpec.js @@ -79,7 +79,7 @@ define( expect(mockPolicyService.allow).toHaveBeenCalledWith( 'composition', mockTypes[0], - mockTypes[1] + mockDomainObjects[1] ); }); diff --git a/platform/containment/test/CompositionPolicySpec.js b/platform/containment/test/CompositionPolicySpec.js index 97659c9bc9..23261ef5de 100644 --- a/platform/containment/test/CompositionPolicySpec.js +++ b/platform/containment/test/CompositionPolicySpec.js @@ -24,60 +24,94 @@ define( ["../src/CompositionPolicy"], function (CompositionPolicy) { describe("Composition policy", function () { - var mockInjector, - mockTypeService, - mockCapabilityService, - mockTypes, + var typeA, + typeB, + typeC, + mockChildObject, policy; beforeEach(function () { - mockInjector = jasmine.createSpyObj('$injector', ['get']); - mockTypeService = jasmine.createSpyObj( - 'typeService', - ['listTypes'] + typeA = jasmine.createSpyObj( + 'type A-- the particular kind', + ['getKey', 'getDefinition'] ); - 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); - mockType.getDefinition.andReturn({ - contains: ['b'] - }); - mockType.getInitialModel.andReturn({}); - return mockType; + typeA.getKey.andReturn('a'); + typeA.getDefinition.andReturn({ + contains: ['a'] }); - mockInjector.get.andCallFake(function (name) { - return { - typeService: mockTypeService, - capabilityService: mockCapabilityService - }[name]; + + typeB = jasmine.createSpyObj( + 'type B-- anything goes', + ['getKey', 'getDefinition'] + ); + typeB.getKey.andReturn('b'); + typeB.getDefinition.andReturn({ + contains: ['a', 'b'] }); - mockTypeService.listTypes.andReturn(mockTypes); - mockCapabilityService.getCapabilities.andReturn({}); + typeC = jasmine.createSpyObj( + '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 - // the unit level in ContainmentTable for 'has' support, et al - it("enforces containment rules defined by types", function () { - expect(policy.allow(mockTypes[0], mockTypes[1])) - .toBeTruthy(); - expect(policy.allow(mockTypes[1], mockTypes[1])) - .toBeTruthy(); - expect(policy.allow(mockTypes[1], mockTypes[0])) - .toBeFalsy(); - expect(policy.allow(mockTypes[0], mockTypes[0])) - .toBeFalsy(); + describe('enforces simple containment rules', function () { + + it('allows when type matches', function () { + mockChildObject.getCapability.andReturn(typeA); + expect(policy.allow(typeA, mockChildObject)) + .toBeTruthy(); + + expect(policy.allow(typeB, mockChildObject)) + .toBeTruthy(); + + 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'); + }); }); }); diff --git a/platform/containment/test/ContainmentTableSpec.js b/platform/containment/test/ContainmentTableSpec.js deleted file mode 100644 index a83d9c1c82..0000000000 --- a/platform/containment/test/ContainmentTableSpec.js +++ /dev/null @@ -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(); - }); - - - }); - } -); diff --git a/platform/entanglement/src/services/CopyService.js b/platform/entanglement/src/services/CopyService.js index fe6a5a43ea..2ac29268cd 100644 --- a/platform/entanglement/src/services/CopyService.js +++ b/platform/entanglement/src/services/CopyService.js @@ -48,7 +48,7 @@ define( return this.policyService.allow( "composition", parentCandidate.getCapability('type'), - object.getCapability('type') + object ); }; diff --git a/platform/entanglement/src/services/LinkService.js b/platform/entanglement/src/services/LinkService.js index 74c985f312..4942f93d35 100644 --- a/platform/entanglement/src/services/LinkService.js +++ b/platform/entanglement/src/services/LinkService.js @@ -52,7 +52,7 @@ define( return this.policyService.allow( "composition", parentCandidate.getCapability('type'), - object.getCapability('type') + object ); }; diff --git a/platform/entanglement/src/services/MoveService.js b/platform/entanglement/src/services/MoveService.js index 592092529f..82c3a70371 100644 --- a/platform/entanglement/src/services/MoveService.js +++ b/platform/entanglement/src/services/MoveService.js @@ -56,7 +56,7 @@ define( return this.policyService.allow( "composition", parentCandidate.getCapability('type'), - object.getCapability('type') + object ); }; diff --git a/platform/entanglement/test/services/CopyServiceSpec.js b/platform/entanglement/test/services/CopyServiceSpec.js index 926e579340..bcd25cf825 100644 --- a/platform/entanglement/test/services/CopyServiceSpec.js +++ b/platform/entanglement/test/services/CopyServiceSpec.js @@ -104,7 +104,7 @@ define( expect(policyService.allow).toHaveBeenCalledWith( "composition", parentCandidate.capabilities.type, - object.capabilities.type + object ); }); diff --git a/platform/entanglement/test/services/LinkServiceSpec.js b/platform/entanglement/test/services/LinkServiceSpec.js index 16d82e6601..6dcd9480b8 100644 --- a/platform/entanglement/test/services/LinkServiceSpec.js +++ b/platform/entanglement/test/services/LinkServiceSpec.js @@ -114,7 +114,7 @@ define( expect(mockPolicyService.allow).toHaveBeenCalledWith( "composition", parentCandidate.capabilities.type, - object.capabilities.type + object ); }); diff --git a/platform/entanglement/test/services/MoveServiceSpec.js b/platform/entanglement/test/services/MoveServiceSpec.js index ee73a9d10c..687559ee56 100644 --- a/platform/entanglement/test/services/MoveServiceSpec.js +++ b/platform/entanglement/test/services/MoveServiceSpec.js @@ -124,7 +124,7 @@ define( expect(policyService.allow).toHaveBeenCalledWith( "composition", parentCandidate.capabilities.type, - object.capabilities.type + object ); }); diff --git a/platform/features/layout/src/LayoutCompositionPolicy.js b/platform/features/layout/src/LayoutCompositionPolicy.js index 4f4d01b81a..a281d2d2ea 100644 --- a/platform/features/layout/src/LayoutCompositionPolicy.js +++ b/platform/features/layout/src/LayoutCompositionPolicy.js @@ -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; diff --git a/platform/features/layout/test/LayoutCompositionPolicySpec.js b/platform/features/layout/test/LayoutCompositionPolicySpec.js index f58a8e5e5c..7d7abb2bcd 100644 --- a/platform/features/layout/test/LayoutCompositionPolicySpec.js +++ b/platform/features/layout/test/LayoutCompositionPolicySpec.js @@ -24,18 +24,25 @@ define( ["../src/LayoutCompositionPolicy"], function (LayoutCompositionPolicy) { describe("Layout's composition policy", function () { - var mockCandidate, + var mockChild, + mockCandidate, mockContext, candidateType, contextType, policy; beforeEach(function () { + mockChild = jasmine.createSpyObj( + 'childObject', + ['getCapability'] + ); mockCandidate = jasmine.createSpyObj('candidateType', ['instanceOf']); mockContext = jasmine.createSpyObj('contextType', ['instanceOf']); + mockChild.getCapability.andReturn(mockContext); + mockCandidate.instanceOf.andCallFake(function (t) { return t === candidateType; }); @@ -49,19 +56,19 @@ define( it("disallows folders in layouts", function () { candidateType = 'layout'; contextType = 'folder'; - expect(policy.allow(mockCandidate, mockContext)).toBe(false); + expect(policy.allow(mockCandidate, mockChild)).toBe(false); }); it("does not disallow folders elsewhere", function () { candidateType = 'nonlayout'; 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 () { candidateType = 'layout'; contextType = 'nonfolder'; - expect(policy.allow(mockCandidate, mockContext)).toBe(true); + expect(policy.allow(mockCandidate, mockChild)).toBe(true); }); }); diff --git a/src/adapter/policies/AdapterCompositionPolicy.js b/src/adapter/policies/AdapterCompositionPolicy.js index 865b08cb59..b9d196e6f0 100644 --- a/src/adapter/policies/AdapterCompositionPolicy.js +++ b/src/adapter/policies/AdapterCompositionPolicy.js @@ -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 ); };