From 1947802a35e1294d48c1587e776818dc79627ccd Mon Sep 17 00:00:00 2001
From: Pete Richards <peter.l.richards@nasa.gov>
Date: Thu, 13 Oct 2016 17:59:31 -0700
Subject: [PATCH] [ObjectAPI] support async root loading

Overload addRoot method to support async root loading.  By supplying a
function you can defer loading to later, and by allowing those functions
to return a promise, execution can be asynchronous.

Remove "removeRoot" as the assumption will be that roots are opt in, and
instead of removing a Root, an application developer should never configure
it in the first place.

Fixes https://github.com/nasa/openmct/issues/1251
---
 src/api/objects/ObjectAPI.js    | 44 ++++++++++--------------
 src/api/objects/RootRegistry.js | 59 +++++++++++++++++++++++++++++++++
 2 files changed, 76 insertions(+), 27 deletions(-)
 create mode 100644 src/api/objects/RootRegistry.js

diff --git a/src/api/objects/ObjectAPI.js b/src/api/objects/ObjectAPI.js
index eb67c7adbd..697716b3be 100644
--- a/src/api/objects/ObjectAPI.js
+++ b/src/api/objects/ObjectAPI.js
@@ -23,11 +23,13 @@
 define([
     'lodash',
     './object-utils',
-    './MutableObject'
+    './MutableObject',
+    './RootRegistry'
 ], function (
     _,
     utils,
-    MutableObject
+    MutableObject,
+    RootRegistry
 ) {
 
 
@@ -40,14 +42,17 @@ define([
 
     function ObjectAPI() {
         this.providers = {};
-        this.rootRegistry = [];
+        this.rootRegistry = new RootRegistry();
         this.rootProvider = {
             'get': function () {
-                return Promise.resolve({
-                    name: 'The root object',
-                    type: 'root',
-                    composition: this.rootRegistry
-                });
+                return this.rootRegistry.getRoots()
+                    .then(function (roots) {
+                        return {
+                            name: 'The root object',
+                            type: 'root',
+                            composition: roots
+                        };
+                    });
             }.bind(this)
         };
     }
@@ -143,29 +148,14 @@ define([
 
     /**
      * 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/RootRegistry.js b/src/api/objects/RootRegistry.js
new file mode 100644
index 0000000000..b2971be176
--- /dev/null
+++ b/src/api/objects/RootRegistry.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([
+    '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);
+    };
+
+
+    RootRegistry.prototype.addRoot = function (key) {
+        if (_.isObject(key) && _.has(key, 'identifier') && _.has(key, 'namespace')) {
+            this.providers.push(function () {
+                return Promise.resolve(key);
+            });
+        } else if (_.isFunction(key)) {
+            this.providers.push(key);
+        }
+    };
+
+    // Assume no one ever removes a root.  everything is opt in.
+    RootRegistry.prototype.removeRoot = function (key) {
+        throw new Error('you should never remove a root');
+    };
+
+    return RootRegistry;
+
+});