Static root plugin not loading after namespace/key changes in OpenMCT #4684

Static root plugin not loading after namespace/key changes in OpenMCT
This commit is contained in:
Khalid Adil 2022-02-28 13:51:34 -06:00 committed by GitHub
parent 4e7debabb1
commit 6a470fba1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 317 additions and 209 deletions

View File

@ -151,7 +151,7 @@ define([
plugins.MyItems = MyItems.default;
plugins.StaticRootPlugin = StaticRootPlugin;
plugins.StaticRootPlugin = StaticRootPlugin.default;
/**
* A tabular view showing the latest values of multiple telemetry points at

View File

@ -1,45 +1,139 @@
define([
'objectUtils'
], function (
objectUtils
) {
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, 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.
*****************************************************************************/
/**
* Transforms an import json blob into a object map that can be used to
* provide objects. Rewrites root identifier in import data with provided
* rootIdentifier, and rewrites all child object identifiers so that they
* exist in the same namespace as the rootIdentifier.
*/
import objectUtils from 'objectUtils';
class StaticModelProvider {
constructor(importData, rootIdentifier) {
this.objectMap = {};
this.rewriteModel(importData, rootIdentifier);
}
/**
* Transforms an import json blob into a object map that can be used to
* provide objects. Rewrites root identifier in import data with provided
* rootIdentifier, and rewrites all child object identifiers so that they
* exist in the same namespace as the rootIdentifier.
* Standard "Get".
*/
function rewriteObjectIdentifiers(importData, rootIdentifier) {
const rootId = importData.rootId;
let objectString = JSON.stringify(importData.openmct);
get(identifier) {
const keyString = objectUtils.makeKeyString(identifier);
if (this.objectMap[keyString]) {
return this.objectMap[keyString];
}
Object.keys(importData.openmct).forEach(function (originalId, i) {
let newId;
if (originalId === rootId) {
newId = objectUtils.makeKeyString(rootIdentifier);
} else {
newId = objectUtils.makeKeyString({
namespace: rootIdentifier.namespace,
key: i
throw new Error(keyString + ' not found in import models.');
}
parseObjectLeaf(objectLeaf, idMap, namespace) {
Object.keys(objectLeaf).forEach((nodeKey) => {
if (idMap.get(nodeKey)) {
const newIdentifier = objectUtils.makeKeyString({
namespace,
key: idMap.get(nodeKey)
});
}
while (objectString.indexOf(originalId) !== -1) {
objectString = objectString.replace(
'"' + originalId + '"',
'"' + newId + '"'
);
objectLeaf[newIdentifier] = { ...objectLeaf[nodeKey] };
delete objectLeaf[nodeKey];
objectLeaf[newIdentifier] = this.parseTreeLeaf(newIdentifier, objectLeaf[newIdentifier], idMap, namespace);
} else {
objectLeaf[nodeKey] = this.parseTreeLeaf(nodeKey, objectLeaf[nodeKey], idMap, namespace);
}
});
return JSON.parse(objectString);
return objectLeaf;
}
parseArrayLeaf(arrayLeaf, idMap, namespace) {
return arrayLeaf.map((leafValue, index) => this.parseTreeLeaf(
null, leafValue, idMap, namespace));
}
parseBranchedLeaf(branchedLeafValue, idMap, namespace) {
if (Array.isArray(branchedLeafValue)) {
return this.parseArrayLeaf(branchedLeafValue, idMap, namespace);
} else {
return this.parseObjectLeaf(branchedLeafValue, idMap, namespace);
}
}
parseTreeLeaf(leafKey, leafValue, idMap, namespace) {
const hasChild = typeof leafValue === 'object';
if (hasChild) {
return this.parseBranchedLeaf(leafValue, idMap, namespace);
}
if (leafKey === 'key') {
return idMap.get(leafValue);
} else if (leafKey === 'namespace') {
return namespace;
} else if (leafKey === 'location') {
if (idMap.get(leafValue)) {
const newLocationIdentifier = objectUtils.makeKeyString({
namespace,
key: idMap.get(leafValue)
});
return newLocationIdentifier;
}
return null;
} else if (idMap.get(leafValue)) {
const newIdentifier = objectUtils.makeKeyString({
namespace,
key: idMap.get(leafValue)
});
return newIdentifier;
} else {
return leafValue;
}
}
rewriteObjectIdentifiers(importData, rootIdentifier) {
const namespace = rootIdentifier.namespace;
const idMap = new Map();
const objectTree = importData.openmct;
Object.keys(objectTree).forEach((originalId, index) => {
let newId = index.toString();
if (originalId === importData.rootId) {
newId = rootIdentifier.key;
}
idMap.set(originalId, newId);
});
const newTree = this.parseTreeLeaf(null, objectTree, idMap, namespace);
return newTree;
}
/**
* Converts all objects in an object make from old format objects to new
* format objects.
*/
function convertToNewObjects(oldObjectMap) {
convertToNewObjects(oldObjectMap) {
return Object.keys(oldObjectMap)
.reduce(function (newObjectMap, key) {
newObjectMap[key] = objectUtils.toNewFormat(oldObjectMap[key], key);
@ -49,7 +143,7 @@ define([
}
/* Set the root location correctly for a top-level object */
function setRootLocation(objectMap, rootIdentifier) {
setRootLocation(objectMap, rootIdentifier) {
objectMap[objectUtils.makeKeyString(rootIdentifier)].location = 'ROOT';
return objectMap;
@ -59,24 +153,11 @@ define([
* Takes importData (as provided by the ImportExport plugin) and exposes
* an object provider to fetch those objects.
*/
function StaticModelProvider(importData, rootIdentifier) {
const oldFormatObjectMap = rewriteObjectIdentifiers(importData, rootIdentifier);
const newFormatObjectMap = convertToNewObjects(oldFormatObjectMap);
this.objectMap = setRootLocation(newFormatObjectMap, rootIdentifier);
rewriteModel(importData, rootIdentifier) {
const oldFormatObjectMap = this.rewriteObjectIdentifiers(importData, rootIdentifier);
const newFormatObjectMap = this.convertToNewObjects(oldFormatObjectMap);
this.objectMap = this.setRootLocation(newFormatObjectMap, rootIdentifier);
}
}
/**
* Standard "Get".
*/
StaticModelProvider.prototype.get = function (identifier) {
const keyString = objectUtils.makeKeyString(identifier);
if (this.objectMap[keyString]) {
return this.objectMap[keyString];
}
throw new Error(keyString + ' not found in import models.');
};
return StaticModelProvider;
});
export default StaticModelProvider;

View File

@ -1,133 +1,149 @@
define([
'./StaticModelProvider',
'./static-provider-test.json'
], function (
StaticModelProvider,
testStaticData
) {
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, 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.
*****************************************************************************/
describe('StaticModelProvider', function () {
import testStaticData from './static-provider-test.json';
import StaticModelProvider from './StaticModelProvider';
let staticProvider;
describe('StaticModelProvider', function () {
let staticProvider;
beforeEach(function () {
const staticData = JSON.parse(JSON.stringify(testStaticData));
staticProvider = new StaticModelProvider(staticData, {
namespace: 'my-import',
key: 'root'
});
});
describe('rootObject', function () {
let rootModel;
beforeEach(function () {
const staticData = JSON.parse(JSON.stringify(testStaticData));
staticProvider = new StaticModelProvider(staticData, {
rootModel = staticProvider.get({
namespace: 'my-import',
key: 'root'
});
});
describe('rootObject', function () {
let rootModel;
it('is located at top level', function () {
expect(rootModel.location).toBe('ROOT');
});
beforeEach(function () {
rootModel = staticProvider.get({
namespace: 'my-import',
key: 'root'
});
});
it('is located at top level', function () {
expect(rootModel.location).toBe('ROOT');
});
it('has new-format identifier', function () {
expect(rootModel.identifier).toEqual({
namespace: 'my-import',
key: 'root'
});
});
it('has new-format composition', function () {
expect(rootModel.composition).toContain({
namespace: 'my-import',
key: '1'
});
expect(rootModel.composition).toContain({
namespace: 'my-import',
key: '2'
});
it('has new-format identifier', function () {
expect(rootModel.identifier).toEqual({
namespace: 'my-import',
key: 'root'
});
});
describe('childObjects', function () {
let swg;
let layout;
let fixed;
beforeEach(function () {
swg = staticProvider.get({
namespace: 'my-import',
key: '1'
});
layout = staticProvider.get({
namespace: 'my-import',
key: '2'
});
fixed = staticProvider.get({
namespace: 'my-import',
key: '3'
});
it('has new-format composition', function () {
expect(rootModel.composition).toContain({
namespace: 'my-import',
key: '1'
});
it('match expected ordering', function () {
// this is a sanity check to make sure the identifiers map in
// the correct order.
expect(swg.type).toBe('generator');
expect(layout.type).toBe('layout');
expect(fixed.type).toBe('telemetry.fixed');
expect(rootModel.composition).toContain({
namespace: 'my-import',
key: '2'
});
it('have new-style identifiers', function () {
expect(swg.identifier).toEqual({
namespace: 'my-import',
key: '1'
});
expect(layout.identifier).toEqual({
namespace: 'my-import',
key: '2'
});
expect(fixed.identifier).toEqual({
namespace: 'my-import',
key: '3'
});
});
it('have new-style composition', function () {
expect(layout.composition).toContain({
namespace: 'my-import',
key: '1'
});
expect(layout.composition).toContain({
namespace: 'my-import',
key: '3'
});
expect(fixed.composition).toContain({
namespace: 'my-import',
key: '1'
});
});
it('rewrites locations', function () {
expect(swg.location).toBe('my-import:root');
expect(layout.location).toBe('my-import:root');
expect(fixed.location).toBe('my-import:2');
});
it('rewrites matched identifiers in objects', function () {
expect(layout.configuration.layout.panels['my-import:1'])
.toBeDefined();
expect(layout.configuration.layout.panels['my-import:3'])
.toBeDefined();
expect(layout.configuration.layout.panels['483c00d4-bb1d-4b42-b29a-c58e06b322a0'])
.not.toBeDefined();
expect(layout.configuration.layout.panels['20273193-f069-49e9-b4f7-b97a87ed755d'])
.not.toBeDefined();
expect(fixed.configuration['fixed-display'].elements[0].id)
.toBe('my-import:1');
});
});
});
describe('childObjects', function () {
let swg;
let layout;
let fixed;
beforeEach(function () {
swg = staticProvider.get({
namespace: 'my-import',
key: '1'
});
layout = staticProvider.get({
namespace: 'my-import',
key: '2'
});
fixed = staticProvider.get({
namespace: 'my-import',
key: '3'
});
});
it('match expected ordering', function () {
// this is a sanity check to make sure the identifiers map in
// the correct order.
expect(swg.type).toBe('generator');
expect(layout.type).toBe('layout');
expect(fixed.type).toBe('telemetry.fixed');
});
it('have new-style identifiers', function () {
expect(swg.identifier).toEqual({
namespace: 'my-import',
key: '1'
});
expect(layout.identifier).toEqual({
namespace: 'my-import',
key: '2'
});
expect(fixed.identifier).toEqual({
namespace: 'my-import',
key: '3'
});
});
it('have new-style composition', function () {
expect(layout.composition).toContain({
namespace: 'my-import',
key: '1'
});
expect(layout.composition).toContain({
namespace: 'my-import',
key: '3'
});
expect(fixed.composition).toContain({
namespace: 'my-import',
key: '1'
});
});
it('rewrites locations', function () {
expect(swg.location).toBe('my-import:root');
expect(layout.location).toBe('my-import:root');
expect(fixed.location).toBe('my-import:2');
});
it('rewrites matched identifiers in objects', function () {
expect(layout.configuration.layout.panels['my-import:1'])
.toBeDefined();
expect(layout.configuration.layout.panels['my-import:3'])
.toBeDefined();
expect(layout.configuration.layout.panels['483c00d4-bb1d-4b42-b29a-c58e06b322a0'])
.not.toBeDefined();
expect(layout.configuration.layout.panels['20273193-f069-49e9-b4f7-b97a87ed755d'])
.not.toBeDefined();
expect(fixed.configuration['fixed-display'].elements[0].id)
.toBe('my-import:1');
});
});
});

View File

@ -1,52 +1,63 @@
define([
'./StaticModelProvider'
], function (
StaticModelProvider
) {
/**
* Static Root Plugin: takes an export file and exposes it as a new root
* object.
*/
function StaticRootPlugin(namespace, exportUrl) {
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, 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.
*****************************************************************************/
const rootIdentifier = {
namespace: namespace,
key: 'root'
};
import StaticModelProvider from './StaticModelProvider';
let cachedProvider;
export default function StaticRootPlugin(options) {
const rootIdentifier = {
namespace: options.namespace,
key: 'root'
};
function loadProvider() {
return fetch(exportUrl)
.then(function (response) {
return response.json();
})
.then(function (importData) {
cachedProvider = new StaticModelProvider(importData, rootIdentifier);
let cachedProvider;
return cachedProvider;
});
}
function loadProvider() {
return fetch(options.exportUrl)
.then(function (response) {
return response.json();
})
.then(function (importData) {
cachedProvider = new StaticModelProvider(importData, rootIdentifier);
function getProvider() {
if (!cachedProvider) {
cachedProvider = loadProvider();
}
return Promise.resolve(cachedProvider);
}
return function install(openmct) {
openmct.objects.addRoot(rootIdentifier);
openmct.objects.addProvider(namespace, {
get: function (identifier) {
return getProvider().then(function (provider) {
return provider.get(identifier);
});
}
return cachedProvider;
});
};
}
return StaticRootPlugin;
});
function getProvider() {
if (!cachedProvider) {
cachedProvider = loadProvider();
}
return Promise.resolve(cachedProvider);
}
return function install(openmct) {
openmct.objects.addRoot(rootIdentifier);
openmct.objects.addProvider(options.namespace, {
get: function (identifier) {
return getProvider().then(function (provider) {
return provider.get(identifier);
});
}
});
};
}