diff --git a/API.md b/API.md index 24083e1cf6..fff93ef2d7 100644 --- a/API.md +++ b/API.md @@ -118,18 +118,11 @@ To do so, use the [`addRoot`]{@link module:openmct.ObjectAPI#addRoot} method of the [object API]{@link module:openmct.ObjectAPI}: ``` -openmct.objects.addRoot({ - identifier: { key: "my-key", namespace: "my-namespace" } - name: "My Root-level Object", - type: "my-type" -}); +openmct.objects.addRoot({ key: "my-key", namespace: "my-namespace" }); ``` -You can also remove this root-level object via its identifier: - -``` -openmct.objects.removeRoot({ key: "my-key", namespace: "my-namespace" }); -``` +Root objects are loaded just like any other objects, i.e. via an object +provider. ### Adding Composition Providers diff --git a/index.html b/index.html index aa5d79cfdc..b6b1715116 100644 --- a/index.html +++ b/index.html @@ -32,7 +32,8 @@ [ 'example/imagery', 'example/eventGenerator', - 'example/generator' + 'example/generator', + 'platform/features/my-items' ].forEach( openmct.legacyRegistry.enable.bind(openmct.legacyRegistry) ); diff --git a/platform/core/bundle.js b/platform/core/bundle.js index 26a49e16d9..55879265c7 100644 --- a/platform/core/bundle.js +++ b/platform/core/bundle.js @@ -24,7 +24,6 @@ define([ "./src/objects/DomainObjectProvider", "./src/capabilities/CoreCapabilityProvider", "./src/models/StaticModelProvider", - "./src/models/RootModelProvider", "./src/models/ModelAggregator", "./src/models/ModelCacheService", "./src/models/PersistedModelProvider", @@ -57,7 +56,6 @@ define([ DomainObjectProvider, CoreCapabilityProvider, StaticModelProvider, - RootModelProvider, ModelAggregator, ModelCacheService, PersistedModelProvider, @@ -152,16 +150,6 @@ define([ "$log" ] }, - { - "provides": "modelService", - "type": "provider", - "implementation": RootModelProvider, - "depends": [ - "roots[]", - "$q", - "$log" - ] - }, { "provides": "modelService", "type": "aggregator", @@ -409,16 +397,6 @@ define([ ] } ], - "roots": [ - { - "id": "mine", - "model": { - "name": "My Items", - "type": "folder", - "composition": [] - } - } - ], "runs": [ { "implementation": TransactingMutationListener, diff --git a/platform/core/src/models/RootModelProvider.js b/platform/core/src/models/RootModelProvider.js deleted file mode 100644 index 205d41e66b..0000000000 --- a/platform/core/src/models/RootModelProvider.js +++ /dev/null @@ -1,79 +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. - *****************************************************************************/ - -/** - * Module defining RootModelProvider. Created by vwoeltje on 11/7/14. - */ -define( - ['./StaticModelProvider'], - function (StaticModelProvider) { - - /** - * Provides the root object (id = "ROOT"), which is the top-level - * domain object shown when the application is started, from which all - * other domain objects are reached. - * - * The root model provider works as the static model provider, - * except that it aggregates roots[] instead of models[], and - * exposes them all as composition of the root object ROOT, - * whose model is also provided by this service. - * - * @memberof platform/core - * @constructor - * @implements {ModelService} - * @param {Array} roots all `roots[]` extensions - * @param $q Angular's $q, for promises - * @param $log Angular's $log, for logging - */ - function RootModelProvider(roots, $q, $log) { - // Pull out identifiers to used as ROOT's - var ids = roots.map(function (root) { - return root.id; - }); - - // Assign an initial location to root models - roots.forEach(function (root) { - if (!root.model) { - root.model = {}; - } - root.model.location = 'ROOT'; - }); - - this.baseProvider = new StaticModelProvider(roots, $q, $log); - this.rootModel = { - name: "The root object", - type: "root", - composition: ids - }; - } - - RootModelProvider.prototype.getModels = function (ids) { - var rootModel = this.rootModel; - return this.baseProvider.getModels(ids).then(function (models) { - models.ROOT = rootModel; - return models; - }); - }; - - return RootModelProvider; - } -); diff --git a/platform/core/test/models/RootModelProviderSpec.js b/platform/core/test/models/RootModelProviderSpec.js deleted file mode 100644 index a7e027c4ab..0000000000 --- a/platform/core/test/models/RootModelProviderSpec.js +++ /dev/null @@ -1,105 +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. - *****************************************************************************/ - -/** - * RootModelProviderSpec. Created by vwoeltje on 11/6/14. - */ -define( - ["../../src/models/RootModelProvider"], - function (RootModelProvider) { - - describe("The root model provider", function () { - var roots = [ - { - "id": "a", - "model": { - "name": "Thing A", - "someProperty": "Some Value A" - } - }, - { - "id": "b", - "model": { - "name": "Thing B", - "someProperty": "Some Value B" - } - } - ], - captured, - mockLog, - mockQ, - provider; - - function mockPromise(value) { - return { - then: function (callback) { - return mockPromise(callback(value)); - } - }; - } - - function capture(value) { - captured = value; - } - - - beforeEach(function () { - mockQ = { when: mockPromise }; - mockLog = jasmine.createSpyObj("$log", ["error", "warn", "info", "debug"]); - provider = new RootModelProvider(roots, mockQ, mockLog); - }); - - it("provides models from extension declarations", function () { - // Verify that we got the promise as the return value - provider.getModels(["a", "b"]).then(capture); - - // Verify that the promise has the desired models - expect(captured.a.name).toEqual("Thing A"); - expect(captured.a.someProperty).toEqual("Some Value A"); - expect(captured.b.name).toEqual("Thing B"); - expect(captured.b.someProperty).toEqual("Some Value B"); - }); - - it("provides models with a location", function () { - provider.getModels(["a", "b"]).then(capture); - expect(captured.a.location).toBe('ROOT'); - expect(captured.b.location).toBe('ROOT'); - }); - - - it("does not provide models which are not in extension declarations", function () { - provider.getModels(["c"]).then(capture); - - // Verify that the promise has the desired models - expect(captured.c).toBeUndefined(); - }); - - it("provides a ROOT object with roots in its composition", function () { - provider.getModels(["ROOT"]).then(capture); - - expect(captured.ROOT).toBeDefined(); - expect(captured.ROOT.composition).toEqual(["a", "b"]); - }); - - }); - } -); diff --git a/platform/features/my-items/bundle.js b/platform/features/my-items/bundle.js new file mode 100644 index 0000000000..42b66ad3b4 --- /dev/null +++ b/platform/features/my-items/bundle.js @@ -0,0 +1,45 @@ +/***************************************************************************** + * 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([ + 'legacyRegistry' +], function ( + legacyRegistry +) { + + legacyRegistry.register("platform/features/my-items", { + "name": "My Items", + "description": "Defines a root named My Items", + "extensions": { + "roots": [ + { + "id": "mine", + "model": { + "name": "My Items", + "type": "folder", + "composition": [] + } + } + ] + } + }); +}); diff --git a/platform/framework/src/register/ServiceCompositor.js b/platform/framework/src/register/ServiceCompositor.js index 7b7f6911da..3c089c6e5a 100644 --- a/platform/framework/src/register/ServiceCompositor.js +++ b/platform/framework/src/register/ServiceCompositor.js @@ -128,6 +128,8 @@ define( latest[service] = name; app.service(name, dependencies.concat([provider])); + + $log.info("Registering provider for " + service); } // Register an array of providers as a single dependency; @@ -138,6 +140,13 @@ define( var name = makeName("provider", service, "*"), list = providerLists[service]; + $log.info([ + "Compositing", + list.length, + "providers for", + service + ].join(" ")); + app.service(name, list.concat([echoMany])); }); } diff --git a/src/api/objects/ObjectAPI.js b/src/api/objects/ObjectAPI.js index eb67c7adbd..05b5caf657 100644 --- a/src/api/objects/ObjectAPI.js +++ b/src/api/objects/ObjectAPI.js @@ -23,11 +23,15 @@ define([ 'lodash', './object-utils', - './MutableObject' + './MutableObject', + './RootRegistry', + './RootObjectProvider' ], function ( _, utils, - MutableObject + MutableObject, + RootRegistry, + RootObjectProvider ) { @@ -35,21 +39,12 @@ define([ * Utilities for loading, saving, and manipulating domain objects. * @interface ObjectAPI * @memberof module:openmct - * @implements {module:openmct.ObjectProvider} */ function ObjectAPI() { this.providers = {}; - this.rootRegistry = []; - this.rootProvider = { - 'get': function () { - return Promise.resolve({ - name: 'The root object', - type: 'root', - composition: this.rootRegistry - }); - }.bind(this) - }; + this.rootRegistry = new RootRegistry(); + this.rootProvider = new RootObjectProvider(this.rootRegistry); } ObjectAPI.prototype.supersecretSetFallbackProvider = function (p) { @@ -57,13 +52,20 @@ define([ }; // Retrieve the provider for a given key. - ObjectAPI.prototype.getProvider = function (key) { - if (key.identifier === 'ROOT') { + ObjectAPI.prototype.getProvider = function (identifier) { + if (identifier.key === 'ROOT') { return this.rootProvider; } - return this.providers[key.namespace] || this.fallbackProvider; + return this.providers[identifier.namespace] || this.fallbackProvider; }; + /** + * Get the root-level object. + * @returns {Promise.} a promise for the root object + */ + ObjectAPI.prototype.getRoot = function () { + return this.rootProvider.get(); + }; /** * Register a new object provider for a particular namespace. @@ -120,14 +122,25 @@ define([ * has been saved, or be rejected if it cannot be saved */ + /** + * Get a domain object. + * + * @method get + * @memberof module:openmct.ObjectAPI# + * @param {module:openmct.ObjectAPI~Identifier} identifier + * the identifier for the domain object to load + * @returns {Promise} a promise which will resolve when the domain object + * has been saved, or be rejected if it cannot be saved + */ + [ 'save', 'delete', 'get' ].forEach(function (method) { ObjectAPI.prototype[method] = function () { - var key = arguments[0], - provider = this.getProvider(key); + var identifier = arguments[0], + provider = this.getProvider(identifier); if (!provider) { throw new Error('No Provider Matched'); @@ -137,35 +150,24 @@ define([ throw new Error('Provider does not support [' + method + '].'); } + if (method === 'get') { + return provider.get(identifier.key); + } + return provider[method].apply(provider, arguments); }; }); /** * Add a root-level object. - * @param {module:openmct.DomainObject} domainObject the root-level object - * to add. + * @param {module:openmct.ObjectAPI~Identifier|function} an array of + * identifiers for root level objects, or a function that returns a + * promise for an identifier or an array of root level objects. * @method addRoot * @memberof module:openmct.ObjectAPI# */ ObjectAPI.prototype.addRoot = function (key) { - this.rootRegistry.unshift(key); - }; - - /** - * Remove a root-level object. - * @param {module:openmct.ObjectAPI~Identifier} id the identifier of the - * root-level object to remove. - * @method removeRoot - * @memberof module:openmct.ObjectAPI# - */ - ObjectAPI.prototype.removeRoot = function (key) { - this.rootRegistry = this.rootRegistry.filter(function (k) { - return ( - k.identifier !== key.identifier || - k.namespace !== key.namespace - ); - }); + this.rootRegistry.addRoot(key); }; /** diff --git a/src/api/objects/RootObjectProvider.js b/src/api/objects/RootObjectProvider.js new file mode 100644 index 0000000000..4b44d66f9c --- /dev/null +++ b/src/api/objects/RootObjectProvider.js @@ -0,0 +1,43 @@ +/***************************************************************************** + * 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 ( +) { + + function RootObjectProvider(rootRegistry) { + this.rootRegistry = rootRegistry; + } + + RootObjectProvider.prototype.get = function () { + return this.rootRegistry.getRoots() + .then(function (roots) { + return { + name: 'The root object', + type: 'root', + composition: roots + }; + }); + }; + + return RootObjectProvider; +}); diff --git a/src/api/objects/RootRegistry.js b/src/api/objects/RootRegistry.js new file mode 100644 index 0000000000..2e04d83410 --- /dev/null +++ b/src/api/objects/RootRegistry.js @@ -0,0 +1,57 @@ +/***************************************************************************** + * 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([ + 'lodash' +], function ( + _ +) { + + function RootRegistry() { + this.providers = []; + } + + RootRegistry.prototype.getRoots = function () { + var promises = this.providers.map(function (provider) { + return provider(); + }); + return Promise.all(promises) + .then(_.flatten); + }; + + function isKey(key) { + return _.isObject(key) && _.has(key, 'key') && _.has(key, 'namespace'); + } + + RootRegistry.prototype.addRoot = function (key) { + if (isKey(key) || (_.isArray(key) && _.every(key, isKey))) { + this.providers.push(function () { + return key; + }); + } else if (_.isFunction(key)) { + this.providers.push(key); + } + }; + + return RootRegistry; + +}); diff --git a/src/api/objects/object-utils.js b/src/api/objects/object-utils.js index f749a8110b..9c37b72911 100644 --- a/src/api/objects/object-utils.js +++ b/src/api/objects/object-utils.js @@ -28,50 +28,50 @@ define([ // take a key string and turn it into a key object // 'scratch:root' ==> {namespace: 'scratch', identifier: 'root'} - var parseKeyString = function (key) { - if (typeof key === 'object') { - return key; + var parseKeyString = function (identifier) { + if (typeof identifier === 'object') { + return identifier; } var namespace = '', - identifier = key; + key = identifier; for (var i = 0, escaped = false; i < key.length; i++) { if (escaped) { escaped = false; namespace += key[i]; } else { - if (key[i] === "\\") { + if (identifier[i] === "\\") { escaped = true; - } else if (key[i] === ":") { + } else if (identifier[i] === ":") { // namespace = key.slice(0, i); - identifier = key.slice(i + 1); + key = identifier.slice(i + 1); break; } - namespace += key[i]; + namespace += identifier[i]; } } - if (key === namespace) { + if (identifier === namespace) { namespace = ''; } return { namespace: namespace, - identifier: identifier + key: key }; }; // take a key and turn it into a key string // {namespace: 'scratch', identifier: 'root'} ==> 'scratch:root' - var makeKeyString = function (key) { - if (typeof key === 'string') { - return key; + var makeKeyString = function (identifier) { + if (typeof identifier === 'string') { + return identifier; } - if (!key.namespace) { - return key.identifier; + if (!identifier.namespace) { + return identifier.key; } return [ - key.namespace.replace(':', '\\:'), - key.identifier.replace(':', '\\:') + identifier.namespace.replace(':', '\\:'), + identifier.key.replace(':', '\\:') ].join(':'); }; diff --git a/src/api/objects/test/RootObjectProviderSpec.js b/src/api/objects/test/RootObjectProviderSpec.js new file mode 100644 index 0000000000..2734c2e55f --- /dev/null +++ b/src/api/objects/test/RootObjectProviderSpec.js @@ -0,0 +1,59 @@ +/***************************************************************************** + * 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([ + '../RootObjectProvider' +], function ( + RootObjectProvider +) { + describe('RootObjectProvider', function () { + var rootRegistry, + rootObjectProvider; + + function done() { + var isDone = false; + waitsFor(function () { + return isDone; + }); + return function () { + isDone = true; + }; + } + + beforeEach(function () { + rootRegistry = jasmine.createSpyObj('rootRegistry', ['getRoots']); + rootRegistry.getRoots.andReturn(Promise.resolve(['some root'])); + rootObjectProvider = new RootObjectProvider(rootRegistry); + }); + + it('supports fetching root', function () { + rootObjectProvider.get() + .then(function (root) { + expect(root).toEqual({ + name: 'The root object', + type: 'root', + composition: ['some root'] + }); + }) + .then(done()); + }); + }); +}); diff --git a/src/api/objects/test/RootRegistrySpec.js b/src/api/objects/test/RootRegistrySpec.js new file mode 100644 index 0000000000..4f918b9eb0 --- /dev/null +++ b/src/api/objects/test/RootRegistrySpec.js @@ -0,0 +1,102 @@ +/***************************************************************************** + * 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([ + '../RootRegistry' +], function ( + RootRegistry +) { + describe('RootRegistry', function () { + var idA, + idB, + idC, + registry; + + function done() { + var isDone = false; + waitsFor(function () { + return isDone; + }); + return function () { + isDone = true; + }; + } + + beforeEach(function () { + idA = {key: 'keyA', namespace: 'something'}; + idB = {key: 'keyB', namespace: 'something'}; + idC = {key: 'keyC', namespace: 'something'}; + registry = new RootRegistry(); + }); + + it('can register a root by key', function () { + registry.addRoot(idA); + registry.getRoots() + .then(function (roots) { + expect(roots).toEqual([idA]); + }) + .then(done()); + }); + + it('can register multiple roots by key', function () { + registry.addRoot([idA, idB]); + registry.getRoots() + .then(function (roots) { + expect(roots).toEqual([idA, idB]); + }) + .then(done()); + }); + + it('can register an asynchronous root ', function () { + registry.addRoot(function () { + return Promise.resolve(idA); + }); + registry.getRoots() + .then(function (roots) { + expect(roots).toEqual([idA]); + }) + .then(done()); + }); + + it('can register multiple asynchronous roots', function () { + registry.addRoot(function () { + return Promise.resolve([idA, idB]); + }); + registry.getRoots() + .then(function (roots) { + expect(roots).toEqual([idA, idB]); + }) + .then(done()); + }); + + it('can combine different types of registration', function () { + registry.addRoot([idA, idB]); + registry.addRoot(function () { + return Promise.resolve([idC]); + }); + registry.getRoots() + .then(function (roots) { + expect(roots).toEqual([idA, idB, idC]); + }) + .then(done()); + }); + }); +}); diff --git a/src/defaultRegistry.js b/src/defaultRegistry.js index a4070313dc..485caacca9 100644 --- a/src/defaultRegistry.js +++ b/src/defaultRegistry.js @@ -72,6 +72,7 @@ define([ '../platform/features/conductor-v2/utcTimeSystem/bundle', '../platform/features/imagery/bundle', '../platform/features/layout/bundle', + '../platform/features/my-items/bundle', '../platform/features/pages/bundle', '../platform/features/plot/bundle', '../platform/features/static-markup/bundle',