mirror of
https://github.com/nasa/openmct.git
synced 2025-06-19 23:53:49 +00:00
* [Composition] provide ids, sync via mutation Composition provides ids, and we sync things via mutation. This simplifies the composition provider interface some, and also fixes some issues with the previous default composition provider related to #1322 fixes #1253 * [Style] Fix style, update jsdoc Fix style, update jsdoc, clean up composition api changes for Fixes #1322 * [Style] Tidy and JSDoc * [Composition] Utilize new composition API Ensures that composition provided by new API works in old API. Some functionality is not present in both places, but for the time being this is sufficient. https://github.com/nasa/openmct/pull/1332 * [Utils] add tests, fix bugs Add tests to objectUtils to ensure correctness. This caught a bug where character escapes were not properly applied or removed. As a result, any missing object that contained a colon in it's key would cause an infinite loop and cause the application to crash. Bug discovered in VISTA integration. * [Style] Fix style * [Roots] Depend on new api for ROOT Depend on new API for ROOT model, ensuring consistency when fetching ROOT model. * [Style] Remove commented code
This commit is contained in:
committed by
Andrew Henry
parent
79b4f9a0f4
commit
73b3ae7264
@ -32,6 +32,10 @@ define([
|
||||
return this.rootRegistry.getRoots()
|
||||
.then(function (roots) {
|
||||
return {
|
||||
identifier: {
|
||||
key: "ROOT",
|
||||
namespace: ""
|
||||
},
|
||||
name: 'The root object',
|
||||
type: 'root',
|
||||
composition: roots
|
||||
|
@ -26,31 +26,50 @@ define([
|
||||
|
||||
) {
|
||||
|
||||
// take a key string and turn it into a key object
|
||||
// 'scratch:root' ==> {namespace: 'scratch', identifier: 'root'}
|
||||
var parseKeyString = function (identifier) {
|
||||
if (typeof identifier === 'object') {
|
||||
return identifier;
|
||||
/**
|
||||
* Utility for checking if a thing is an Open MCT Identifier.
|
||||
* @private
|
||||
*/
|
||||
function isIdentifier(thing) {
|
||||
return typeof thing === 'object' &&
|
||||
thing.hasOwnProperty('key') &&
|
||||
thing.hasOwnProperty('namespace');
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility for checking if a thing is a key string. Not perfect.
|
||||
* @private
|
||||
*/
|
||||
function isKeyString(thing) {
|
||||
return typeof thing === 'string';
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a keyString into an Open MCT Identifier, ex:
|
||||
* 'scratch:root' ==> {namespace: 'scratch', key: 'root'}
|
||||
*
|
||||
* Idempotent.
|
||||
*
|
||||
* @param keyString
|
||||
* @returns identifier
|
||||
*/
|
||||
function parseKeyString(keyString) {
|
||||
if (isIdentifier(keyString)) {
|
||||
return keyString;
|
||||
}
|
||||
var namespace = '',
|
||||
key = identifier;
|
||||
for (var i = 0, escaped = false; i < key.length; i++) {
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
namespace += key[i];
|
||||
} else {
|
||||
if (identifier[i] === "\\") {
|
||||
escaped = true;
|
||||
} else if (identifier[i] === ":") {
|
||||
// namespace = key.slice(0, i);
|
||||
key = identifier.slice(i + 1);
|
||||
break;
|
||||
}
|
||||
namespace += identifier[i];
|
||||
key = keyString;
|
||||
for (var i = 0; i < key.length; i++) {
|
||||
if (key[i] === "\\" && key[i + 1] === ":") {
|
||||
i++; // skip escape character.
|
||||
} else if (key[i] === ":") {
|
||||
key = key.slice(i + 1);
|
||||
break;
|
||||
}
|
||||
namespace += key[i];
|
||||
}
|
||||
|
||||
if (identifier === namespace) {
|
||||
if (keyString === namespace) {
|
||||
namespace = '';
|
||||
}
|
||||
|
||||
@ -58,52 +77,95 @@ define([
|
||||
namespace: namespace,
|
||||
key: key
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// take a key and turn it into a key string
|
||||
// {namespace: 'scratch', identifier: 'root'} ==> 'scratch:root'
|
||||
var makeKeyString = function (identifier) {
|
||||
if (typeof identifier === 'string') {
|
||||
|
||||
/**
|
||||
* Convert an Open MCT Identifier into a keyString, ex:
|
||||
* {namespace: 'scratch', key: 'root'} ==> 'scratch:root'
|
||||
*
|
||||
* Idempotent
|
||||
*
|
||||
* @param identifier
|
||||
* @returns keyString
|
||||
*/
|
||||
function makeKeyString(identifier) {
|
||||
if (isKeyString(identifier)) {
|
||||
return identifier;
|
||||
}
|
||||
if (!identifier.namespace) {
|
||||
return identifier.key;
|
||||
}
|
||||
return [
|
||||
identifier.namespace.replace(':', '\\:'),
|
||||
identifier.key.replace(':', '\\:')
|
||||
identifier.namespace.replace(/\:/g, '\\:'),
|
||||
identifier.key
|
||||
].join(':');
|
||||
};
|
||||
}
|
||||
|
||||
// Converts composition to use key strings instead of keys
|
||||
var toOldFormat = function (model) {
|
||||
/**
|
||||
* Convert a new domain object into an old format model, removing the
|
||||
* identifier and converting the composition array from Open MCT Identifiers
|
||||
* to old format keyStrings.
|
||||
*
|
||||
* @param domainObject
|
||||
* @returns oldFormatModel
|
||||
*/
|
||||
function toOldFormat(model) {
|
||||
model = JSON.parse(JSON.stringify(model));
|
||||
delete model.identifier;
|
||||
if (model.composition) {
|
||||
model.composition = model.composition.map(makeKeyString);
|
||||
}
|
||||
return model;
|
||||
};
|
||||
}
|
||||
|
||||
// converts composition to use keys instead of key strings
|
||||
var toNewFormat = function (model, identifier) {
|
||||
/**
|
||||
* Convert an old format domain object model into a new format domain
|
||||
* object. Adds an identifier using the provided keyString, and converts
|
||||
* the composition array to utilize Open MCT Identifiers.
|
||||
*
|
||||
* @param model
|
||||
* @param keyString
|
||||
* @returns domainObject
|
||||
*/
|
||||
function toNewFormat(model, keyString) {
|
||||
model = JSON.parse(JSON.stringify(model));
|
||||
model.identifier = parseKeyString(identifier);
|
||||
model.identifier = parseKeyString(keyString);
|
||||
if (model.composition) {
|
||||
model.composition = model.composition.map(parseKeyString);
|
||||
}
|
||||
return model;
|
||||
};
|
||||
}
|
||||
|
||||
var equals = function (a, b) {
|
||||
return makeKeyString(a.key) === makeKeyString(b.key);
|
||||
};
|
||||
/**
|
||||
* Compare two Open MCT Identifiers, returning true if they are equal.
|
||||
*
|
||||
* @param identifier
|
||||
* @param otherIdentifier
|
||||
* @returns Boolean true if identifiers are equal.
|
||||
*/
|
||||
function identifierEquals(a, b) {
|
||||
return a.key === b.key && a.namespace === b.namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two domain objects, return true if they're the same object.
|
||||
* Equality is determined by identifier.
|
||||
*
|
||||
* @param domainObject
|
||||
* @param otherDomainOBject
|
||||
* @returns Boolean true if objects are equal.
|
||||
*/
|
||||
function objectEquals(a, b) {
|
||||
return identifierEquals(a.identifier, b.identifier);
|
||||
}
|
||||
|
||||
return {
|
||||
toOldFormat: toOldFormat,
|
||||
toNewFormat: toNewFormat,
|
||||
makeKeyString: makeKeyString,
|
||||
parseKeyString: parseKeyString,
|
||||
equals: equals
|
||||
equals: objectEquals,
|
||||
identifierEquals: identifierEquals
|
||||
};
|
||||
});
|
||||
|
@ -48,6 +48,10 @@ define([
|
||||
rootObjectProvider.get()
|
||||
.then(function (root) {
|
||||
expect(root).toEqual({
|
||||
identifier: {
|
||||
key: "ROOT",
|
||||
namespace: ""
|
||||
},
|
||||
name: 'The root object',
|
||||
type: 'root',
|
||||
composition: ['some root']
|
||||
|
153
src/api/objects/test/object-utilsSpec.js
Normal file
153
src/api/objects/test/object-utilsSpec.js
Normal file
@ -0,0 +1,153 @@
|
||||
define([
|
||||
'../object-utils'
|
||||
], function (
|
||||
objectUtils
|
||||
) {
|
||||
describe('objectUtils', function () {
|
||||
|
||||
|
||||
describe('keyString util', function () {
|
||||
var EXPECTATIONS = {
|
||||
'ROOT': {
|
||||
namespace: '',
|
||||
key: 'ROOT'
|
||||
},
|
||||
'mine': {
|
||||
namespace: '',
|
||||
key: 'mine'
|
||||
},
|
||||
'extended:something:with:colons': {
|
||||
key: 'something:with:colons',
|
||||
namespace: 'extended'
|
||||
},
|
||||
'https\\://some/url:resourceId': {
|
||||
key: 'resourceId',
|
||||
namespace: 'https://some/url'
|
||||
},
|
||||
'scratch:root': {
|
||||
namespace: 'scratch',
|
||||
key: 'root'
|
||||
},
|
||||
'thingy\\:thing:abc123': {
|
||||
namespace: 'thingy:thing',
|
||||
key: 'abc123'
|
||||
}
|
||||
};
|
||||
|
||||
Object.keys(EXPECTATIONS).forEach(function (keyString) {
|
||||
it('parses "' + keyString + '".', function () {
|
||||
expect(objectUtils.parseKeyString(keyString))
|
||||
.toEqual(EXPECTATIONS[keyString]);
|
||||
});
|
||||
|
||||
it('parses and re-encodes "' + keyString + '"', function () {
|
||||
var identifier = objectUtils.parseKeyString(keyString);
|
||||
expect(objectUtils.makeKeyString(identifier))
|
||||
.toEqual(keyString);
|
||||
});
|
||||
|
||||
it('is idempotent for "' + keyString + '".', function () {
|
||||
var identifier = objectUtils.parseKeyString(keyString);
|
||||
var again = objectUtils.parseKeyString(identifier);
|
||||
expect(identifier).toEqual(again);
|
||||
again = objectUtils.parseKeyString(again);
|
||||
again = objectUtils.parseKeyString(again);
|
||||
expect(identifier).toEqual(again);
|
||||
|
||||
var againKeyString = objectUtils.makeKeyString(again);
|
||||
expect(againKeyString).toEqual(keyString);
|
||||
againKeyString = objectUtils.makeKeyString(againKeyString);
|
||||
againKeyString = objectUtils.makeKeyString(againKeyString);
|
||||
againKeyString = objectUtils.makeKeyString(againKeyString);
|
||||
expect(againKeyString).toEqual(keyString);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('old object conversions', function () {
|
||||
|
||||
it('translate ids', function () {
|
||||
expect(objectUtils.toNewFormat({
|
||||
prop: 'someValue'
|
||||
}, 'objId'))
|
||||
.toEqual({
|
||||
prop: 'someValue',
|
||||
identifier: {
|
||||
namespace: '',
|
||||
key: 'objId'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('translates composition', function () {
|
||||
expect(objectUtils.toNewFormat({
|
||||
prop: 'someValue',
|
||||
composition: [
|
||||
'anotherObjectId',
|
||||
'scratch:anotherObjectId'
|
||||
]
|
||||
}, 'objId'))
|
||||
.toEqual({
|
||||
prop: 'someValue',
|
||||
composition: [
|
||||
{
|
||||
namespace: '',
|
||||
key: 'anotherObjectId'
|
||||
},
|
||||
{
|
||||
namespace: 'scratch',
|
||||
key: 'anotherObjectId'
|
||||
}
|
||||
],
|
||||
identifier: {
|
||||
namespace: '',
|
||||
key: 'objId'
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('new object conversions', function () {
|
||||
|
||||
it('removes ids', function () {
|
||||
expect(objectUtils.toOldFormat({
|
||||
prop: 'someValue',
|
||||
identifier: {
|
||||
namespace: '',
|
||||
key: 'objId'
|
||||
}
|
||||
}))
|
||||
.toEqual({
|
||||
prop: 'someValue'
|
||||
});
|
||||
});
|
||||
|
||||
it('translates composition', function () {
|
||||
expect(objectUtils.toOldFormat({
|
||||
prop: 'someValue',
|
||||
composition: [
|
||||
{
|
||||
namespace: '',
|
||||
key: 'anotherObjectId'
|
||||
},
|
||||
{
|
||||
namespace: 'scratch',
|
||||
key: 'anotherObjectId'
|
||||
}
|
||||
],
|
||||
identifier: {
|
||||
namespace: '',
|
||||
key: 'objId'
|
||||
}
|
||||
}))
|
||||
.toEqual({
|
||||
prop: 'someValue',
|
||||
composition: [
|
||||
'anotherObjectId',
|
||||
'scratch:anotherObjectId'
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user