[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:
46
API.md
46
API.md
@@ -52,6 +52,8 @@
|
|||||||
- [The URL Status Indicator](#the-url-status-indicator)
|
- [The URL Status Indicator](#the-url-status-indicator)
|
||||||
- [Creating a Simple Indicator](#creating-a-simple-indicator)
|
- [Creating a Simple Indicator](#creating-a-simple-indicator)
|
||||||
- [Custom Indicators](#custom-indicators)
|
- [Custom Indicators](#custom-indicators)
|
||||||
|
- [Priority API](#priority-api)
|
||||||
|
- [Priority Types](#priority-types)
|
||||||
|
|
||||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
<!-- 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.
|
eg.
|
||||||
```javascript
|
```javascript
|
||||||
openmct.objects.addRoot({
|
openmct.objects.addRoot({
|
||||||
namespace: "example.namespace",
|
namespace: "example.namespace",
|
||||||
key: "my-key"
|
key: "my-key"
|
||||||
});
|
},
|
||||||
|
openmct.priority.HIGH);
|
||||||
```
|
```
|
||||||
|
|
||||||
The `addRoot` function takes a single [object identifier](#domain-objects-and-identifiers)
|
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
|
||||||
as an argument.
|
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
|
When using the `getAll` method of the object API, they will be returned in order of priority.
|
||||||
provider.
|
|
||||||
|
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
|
## Object Providers
|
||||||
|
|
||||||
@@ -1051,3 +1061,25 @@ A completely custom indicator can be added by simply providing a DOM element to
|
|||||||
element: domNode
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -41,7 +41,7 @@ function ObjectAPI(typeRegistry, openmct) {
|
|||||||
this.typeRegistry = typeRegistry;
|
this.typeRegistry = typeRegistry;
|
||||||
this.eventEmitter = new EventEmitter();
|
this.eventEmitter = new EventEmitter();
|
||||||
this.providers = {};
|
this.providers = {};
|
||||||
this.rootRegistry = new RootRegistry();
|
this.rootRegistry = new RootRegistry(openmct);
|
||||||
this.inMemorySearchProvider = new InMemorySearchProvider(openmct);
|
this.inMemorySearchProvider = new InMemorySearchProvider(openmct);
|
||||||
|
|
||||||
this.rootProvider = new RootObjectProvider(this.rootRegistry);
|
this.rootProvider = new RootObjectProvider(this.rootRegistry);
|
||||||
@@ -367,14 +367,17 @@ ObjectAPI.prototype.endTransaction = function () {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a root-level object.
|
* Add a root-level object.
|
||||||
* @param {module:openmct.ObjectAPI~Identifier|function} an array of
|
* @param {module:openmct.ObjectAPI~Identifier|array|function} identifier an identifier or
|
||||||
* identifiers for root level objects, or a function that returns a
|
* 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.
|
* 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
|
* @method addRoot
|
||||||
* @memberof module:openmct.ObjectAPI#
|
* @memberof module:openmct.ObjectAPI#
|
||||||
*/
|
*/
|
||||||
ObjectAPI.prototype.addRoot = function (key) {
|
ObjectAPI.prototype.addRoot = function (identifier, priority) {
|
||||||
this.rootRegistry.addRoot(key);
|
this.rootRegistry.addRoot(identifier, priority);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -20,39 +20,43 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([
|
import utils from './object-utils';
|
||||||
'lodash'
|
|
||||||
], function (
|
|
||||||
_
|
|
||||||
) {
|
|
||||||
|
|
||||||
function RootRegistry() {
|
export default class RootRegistry {
|
||||||
this.providers = [];
|
|
||||||
|
constructor(openmct) {
|
||||||
|
this._rootItems = [];
|
||||||
|
this._openmct = openmct;
|
||||||
}
|
}
|
||||||
|
|
||||||
RootRegistry.prototype.getRoots = function () {
|
getRoots() {
|
||||||
const promises = this.providers.map(function (provider) {
|
const sortedItems = this._rootItems.sort((a, b) => b.priority - a.priority);
|
||||||
return provider();
|
const promises = sortedItems.map((rootItem) => rootItem.provider());
|
||||||
});
|
|
||||||
|
|
||||||
return Promise.all(promises)
|
return Promise.all(promises).then(rootItems => rootItems.flat());
|
||||||
.then(_.flatten);
|
|
||||||
};
|
|
||||||
|
|
||||||
function isKey(key) {
|
|
||||||
return _.isObject(key) && _.has(key, 'key') && _.has(key, 'namespace');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RootRegistry.prototype.addRoot = function (key) {
|
addRoot(rootItem, priority) {
|
||||||
if (isKey(key) || (Array.isArray(key) && key.every(isKey))) {
|
|
||||||
this.providers.push(function () {
|
if (!this._isValid(rootItem)) {
|
||||||
return key;
|
return;
|
||||||
});
|
|
||||||
} else if (typeof key === "function") {
|
|
||||||
this.providers.push(key);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -172,6 +172,7 @@ define([
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
isIdentifier: isIdentifier,
|
||||||
toOldFormat: toOldFormat,
|
toOldFormat: toOldFormat,
|
||||||
toNewFormat: toNewFormat,
|
toNewFormat: toNewFormat,
|
||||||
makeKeyString: makeKeyString,
|
makeKeyString: makeKeyString,
|
||||||
|
|||||||
@@ -19,83 +19,113 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* 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 () {
|
import { createOpenMct, resetApplicationState } from '../../../utils/testing';
|
||||||
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 () {
|
describe('RootRegistry', () => {
|
||||||
registry.addRoot(idA);
|
let openmct;
|
||||||
|
let idA;
|
||||||
|
let idB;
|
||||||
|
let idC;
|
||||||
|
let idD;
|
||||||
|
|
||||||
return registry.getRoots()
|
beforeEach((done) => {
|
||||||
.then(function (roots) {
|
openmct = createOpenMct();
|
||||||
expect(roots).toEqual([idA]);
|
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 () {
|
openmct.on('start', done);
|
||||||
registry.addRoot([idA, idB]);
|
openmct.startHeadless();
|
||||||
|
});
|
||||||
|
|
||||||
return registry.getRoots()
|
afterEach(async () => {
|
||||||
.then(function (roots) {
|
await resetApplicationState(openmct);
|
||||||
expect(roots).toEqual([idA, idB]);
|
});
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can register an asynchronous root ', function () {
|
it('can register a root by identifier', () => {
|
||||||
registry.addRoot(function () {
|
openmct.objects.addRoot(idA);
|
||||||
return Promise.resolve(idA);
|
|
||||||
|
return openmct.objects.getRoot()
|
||||||
|
.then((rootObject) => {
|
||||||
|
expect(rootObject.composition).toEqual([idA]);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return registry.getRoots()
|
it('can register multiple roots by identifier', () => {
|
||||||
.then(function (roots) {
|
openmct.objects.addRoot([idA, idB]);
|
||||||
expect(roots).toEqual([idA]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can register multiple asynchronous roots', function () {
|
return openmct.objects.getRoot()
|
||||||
registry.addRoot(function () {
|
.then((rootObject) => {
|
||||||
return Promise.resolve([idA, idB]);
|
expect(rootObject.composition).toEqual([idA, idB]);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return registry.getRoots()
|
it('can register an asynchronous root ', () => {
|
||||||
.then(function (roots) {
|
openmct.objects.addRoot(() => Promise.resolve(idA));
|
||||||
expect(roots).toEqual([idA, idB]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can combine different types of registration', function () {
|
return openmct.objects.getRoot()
|
||||||
registry.addRoot([idA, idB]);
|
.then((rootObject) => {
|
||||||
registry.addRoot(function () {
|
expect(rootObject.composition).toEqual([idA]);
|
||||||
return Promise.resolve([idC]);
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return registry.getRoots()
|
it('can register multiple asynchronous roots', () => {
|
||||||
.then(function (roots) {
|
openmct.objects.addRoot(() => Promise.resolve([idA, idB]));
|
||||||
expect(roots).toEqual([idA, idB, idC]);
|
|
||||||
});
|
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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,11 +25,15 @@ import myItemsInterceptor from "./myItemsInterceptor";
|
|||||||
|
|
||||||
const MY_ITEMS_DEFAULT_NAME = 'My Items';
|
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) {
|
return function install(openmct) {
|
||||||
const identifier = createMyItemsIdentifier(namespace);
|
const identifier = createMyItemsIdentifier(namespace);
|
||||||
|
|
||||||
|
if (priority === undefined) {
|
||||||
|
priority = openmct.priority.LOW;
|
||||||
|
}
|
||||||
|
|
||||||
openmct.objects.addGetInterceptor(myItemsInterceptor(openmct, identifier, name));
|
openmct.objects.addGetInterceptor(myItemsInterceptor(openmct, identifier, name));
|
||||||
openmct.objects.addRoot(identifier);
|
openmct.objects.addRoot(identifier, priority);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user