[Root Objects] Order by specified priority (#4658)

* Updated objectAPI to support root priority
* Updated to new ES6 module for root registry and updated docs for new priority API and root object priority
* Set "My Items" to default priority of low, for root object order

Co-authored-by: Andrew Henry <akhenry@gmail.com>
This commit is contained in:
Jamie V 2022-01-04 16:34:48 -08:00 committed by GitHub
parent d53ca3ec9a
commit 2fc0d34b8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 179 additions and 105 deletions

46
API.md
View File

@ -52,6 +52,8 @@
- [The URL Status Indicator](#the-url-status-indicator)
- [Creating a Simple Indicator](#creating-a-simple-indicator)
- [Custom Indicators](#custom-indicators)
- [Priority API](#priority-api)
- [Priority Types](#priority-types)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@ -247,16 +249,24 @@ To do so, use the `addRoot` method of the object API.
eg.
```javascript
openmct.objects.addRoot({
namespace: "example.namespace",
key: "my-key"
});
namespace: "example.namespace",
key: "my-key"
},
openmct.priority.HIGH);
```
The `addRoot` function takes a single [object identifier](#domain-objects-and-identifiers)
as an argument.
The `addRoot` function takes a two arguments, the first can be an [object identifier](#domain-objects-and-identifiers) for a root level object, or 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, the second is a [priority](#priority-api) or numeric value.
Root objects are loaded just like any other objects, i.e. via an object
provider.
When using the `getAll` method of the object API, they will be returned in order of priority.
eg.
```javascript
openmct.objects.addRoot(identifier, openmct.priority.LOW); // low = -1000, will appear last in composition or tree
openmct.objects.addRoot(otherIdentifier, openmct.priority.HIGH); // high = 1000, will appear first in composition or tree
```
Root objects are loaded just like any other objects, i.e. via an object provider.
## Object Providers
@ -1051,3 +1061,25 @@ A completely custom indicator can be added by simply providing a DOM element to
element: domNode
});
```
## Priority API
Open MCT provides some built-in priority values that can be used in the application for view providers, indicators, root object order, and more.
### Priority Types
Currently, the Open MCT Priority API provides (type: numeric value):
- HIGH: 1000
- Default: 0
- LOW: -1000
View provider Example:
``` javascript
class ViewProvider {
...
priority() {
return openmct.priority.HIGH;
}
}
```

View File

@ -41,7 +41,7 @@ function ObjectAPI(typeRegistry, openmct) {
this.typeRegistry = typeRegistry;
this.eventEmitter = new EventEmitter();
this.providers = {};
this.rootRegistry = new RootRegistry();
this.rootRegistry = new RootRegistry(openmct);
this.inMemorySearchProvider = new InMemorySearchProvider(openmct);
this.rootProvider = new RootObjectProvider(this.rootRegistry);
@ -367,14 +367,17 @@ ObjectAPI.prototype.endTransaction = function () {
/**
* Add a root-level object.
* @param {module:openmct.ObjectAPI~Identifier|function} an array of
* identifiers for root level objects, or a function that returns a
* @param {module:openmct.ObjectAPI~Identifier|array|function} identifier an identifier or
* 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.
* @param {module:openmct.PriorityAPI~priority|Number} priority a number representing
* this item(s) position in the root object's composition (example: order in object tree).
* For arrays, they are treated as blocks.
* @method addRoot
* @memberof module:openmct.ObjectAPI#
*/
ObjectAPI.prototype.addRoot = function (key) {
this.rootRegistry.addRoot(key);
ObjectAPI.prototype.addRoot = function (identifier, priority) {
this.rootRegistry.addRoot(identifier, priority);
};
/**

View File

@ -20,39 +20,43 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
'lodash'
], function (
_
) {
import utils from './object-utils';
function RootRegistry() {
this.providers = [];
export default class RootRegistry {
constructor(openmct) {
this._rootItems = [];
this._openmct = openmct;
}
RootRegistry.prototype.getRoots = function () {
const promises = this.providers.map(function (provider) {
return provider();
});
getRoots() {
const sortedItems = this._rootItems.sort((a, b) => b.priority - a.priority);
const promises = sortedItems.map((rootItem) => rootItem.provider());
return Promise.all(promises)
.then(_.flatten);
};
function isKey(key) {
return _.isObject(key) && _.has(key, 'key') && _.has(key, 'namespace');
return Promise.all(promises).then(rootItems => rootItems.flat());
}
RootRegistry.prototype.addRoot = function (key) {
if (isKey(key) || (Array.isArray(key) && key.every(isKey))) {
this.providers.push(function () {
return key;
});
} else if (typeof key === "function") {
this.providers.push(key);
addRoot(rootItem, priority) {
if (!this._isValid(rootItem)) {
return;
}
};
return RootRegistry;
this._rootItems.push({
priority: priority || this._openmct.priority.DEFAULT,
provider: typeof rootItem === 'function' ? rootItem : () => rootItem
});
}
});
_isValid(rootItem) {
if (utils.isIdentifier(rootItem) || typeof rootItem === 'function') {
return true;
}
if (Array.isArray(rootItem)) {
return rootItem.every(utils.isIdentifier);
}
return false;
}
}

View File

@ -172,6 +172,7 @@ define([
}
return {
isIdentifier: isIdentifier,
toOldFormat: toOldFormat,
toNewFormat: toNewFormat,
makeKeyString: makeKeyString,

View File

@ -19,83 +19,113 @@
* 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 () {
let idA;
let idB;
let idC;
let registry;
beforeEach(function () {
idA = {
key: 'keyA',
namespace: 'something'
};
idB = {
key: 'keyB',
namespace: 'something'
};
idC = {
key: 'keyC',
namespace: 'something'
};
registry = new RootRegistry();
});
import { createOpenMct, resetApplicationState } from '../../../utils/testing';
it('can register a root by key', function () {
registry.addRoot(idA);
describe('RootRegistry', () => {
let openmct;
let idA;
let idB;
let idC;
let idD;
return registry.getRoots()
.then(function (roots) {
expect(roots).toEqual([idA]);
});
});
beforeEach((done) => {
openmct = createOpenMct();
idA = {
key: 'keyA',
namespace: 'something'
};
idB = {
key: 'keyB',
namespace: 'something'
};
idC = {
key: 'keyC',
namespace: 'something'
};
idD = {
key: 'keyD',
namespace: 'something'
};
it('can register multiple roots by key', function () {
registry.addRoot([idA, idB]);
openmct.on('start', done);
openmct.startHeadless();
});
return registry.getRoots()
.then(function (roots) {
expect(roots).toEqual([idA, idB]);
});
});
afterEach(async () => {
await resetApplicationState(openmct);
});
it('can register an asynchronous root ', function () {
registry.addRoot(function () {
return Promise.resolve(idA);
it('can register a root by identifier', () => {
openmct.objects.addRoot(idA);
return openmct.objects.getRoot()
.then((rootObject) => {
expect(rootObject.composition).toEqual([idA]);
});
});
return registry.getRoots()
.then(function (roots) {
expect(roots).toEqual([idA]);
});
});
it('can register multiple roots by identifier', () => {
openmct.objects.addRoot([idA, idB]);
it('can register multiple asynchronous roots', function () {
registry.addRoot(function () {
return Promise.resolve([idA, idB]);
return openmct.objects.getRoot()
.then((rootObject) => {
expect(rootObject.composition).toEqual([idA, idB]);
});
});
return registry.getRoots()
.then(function (roots) {
expect(roots).toEqual([idA, idB]);
});
});
it('can register an asynchronous root ', () => {
openmct.objects.addRoot(() => Promise.resolve(idA));
it('can combine different types of registration', function () {
registry.addRoot([idA, idB]);
registry.addRoot(function () {
return Promise.resolve([idC]);
return openmct.objects.getRoot()
.then((rootObject) => {
expect(rootObject.composition).toEqual([idA]);
});
});
return registry.getRoots()
.then(function (roots) {
expect(roots).toEqual([idA, idB, idC]);
});
});
it('can register multiple asynchronous roots', () => {
openmct.objects.addRoot(() => Promise.resolve([idA, idB]));
return openmct.objects.getRoot()
.then((rootObject) => {
expect(rootObject.composition).toEqual([idA, idB]);
});
});
it('can combine different types of registration', () => {
openmct.objects.addRoot([idA, idB]);
openmct.objects.addRoot(() => Promise.resolve([idC]));
return openmct.objects.getRoot()
.then((rootObject) => {
expect(rootObject.composition).toEqual([idA, idB, idC]);
});
});
it('supports priority ordering for identifiers', () => {
openmct.objects.addRoot(idA, openmct.priority.LOW);
openmct.objects.addRoot(idB, openmct.priority.HIGH);
openmct.objects.addRoot(idC); // DEFAULT
return openmct.objects.getRoot()
.then((rootObject) => {
expect(rootObject.composition[0]).toEqual(idB);
expect(rootObject.composition[1]).toEqual(idC);
expect(rootObject.composition[2]).toEqual(idA);
});
});
it('supports priority ordering for different types of registration', () => {
openmct.objects.addRoot(() => Promise.resolve([idC]), openmct.priority.LOW);
openmct.objects.addRoot(idB, openmct.priority.HIGH);
openmct.objects.addRoot([idA, idD]); // default
return openmct.objects.getRoot()
.then((rootObject) => {
expect(rootObject.composition[0]).toEqual(idB);
expect(rootObject.composition[1]).toEqual(idA);
expect(rootObject.composition[2]).toEqual(idD);
expect(rootObject.composition[3]).toEqual(idC);
});
});
});

View File

@ -25,11 +25,15 @@ import myItemsInterceptor from "./myItemsInterceptor";
const MY_ITEMS_DEFAULT_NAME = 'My Items';
export default function MyItemsPlugin(name = MY_ITEMS_DEFAULT_NAME, namespace = '') {
export default function MyItemsPlugin(name = MY_ITEMS_DEFAULT_NAME, namespace = '', priority = undefined) {
return function install(openmct) {
const identifier = createMyItemsIdentifier(namespace);
if (priority === undefined) {
priority = openmct.priority.LOW;
}
openmct.objects.addGetInterceptor(myItemsInterceptor(openmct, identifier, name));
openmct.objects.addRoot(identifier);
openmct.objects.addRoot(identifier, priority);
};
}