Master 2.0.7 (#5672)

This commit is contained in:
Shefali Joshi
2022-08-22 11:41:38 -07:00
committed by GitHub
parent efadf9036f
commit 291e62687e
29 changed files with 805 additions and 228 deletions

View File

@ -40,6 +40,8 @@ const ANNOTATION_TYPES = Object.freeze({
PLOT_SPATIAL: 'PLOT_SPATIAL'
});
const ANNOTATION_TYPE = 'annotation';
/**
* @typedef {Object} Tag
* @property {String} key a unique identifier for the tag
@ -54,7 +56,7 @@ export default class AnnotationAPI extends EventEmitter {
this.ANNOTATION_TYPES = ANNOTATION_TYPES;
this.openmct.types.addType('annotation', {
this.openmct.types.addType(ANNOTATION_TYPE, {
name: 'Annotation',
description: 'A user created note or comment about time ranges, pixel space, and geospatial features.',
creatable: false,
@ -136,6 +138,10 @@ export default class AnnotationAPI extends EventEmitter {
this.availableTags[tagKey] = tagsDefinition;
}
isAnnotation(domainObject) {
return domainObject && (domainObject.type === ANNOTATION_TYPE);
}
getAvailableTags() {
if (this.availableTags) {
const rearrangedToArray = Object.keys(this.availableTags).map(tagKey => {
@ -271,7 +277,10 @@ export default class AnnotationAPI extends EventEmitter {
const searchResults = (await Promise.all(this.openmct.objects.search(matchingTagKeys, abortController, this.openmct.objects.SEARCH_TYPES.TAGS))).flat();
const appliedTagSearchResults = this.#addTagMetaInformationToResults(searchResults, matchingTagKeys);
const appliedTargetsModels = await this.#addTargetModelsToResults(appliedTagSearchResults);
const resultsWithValidPath = appliedTargetsModels.filter(result => {
return this.openmct.objects.isReachable(result.targetModels?.[0]?.originalPath);
});
return appliedTargetsModels;
return resultsWithValidPath;
}
}

View File

@ -27,15 +27,26 @@ describe("The Annotation API", () => {
let openmct;
let mockObjectProvider;
let mockDomainObject;
let mockFolderObject;
let mockAnnotationObject;
beforeEach((done) => {
openmct = createOpenMct();
openmct.install(new ExampleTagsPlugin());
const availableTags = openmct.annotation.getAvailableTags();
mockFolderObject = {
type: 'root',
name: 'folderFoo',
location: '',
identifier: {
key: 'someParent',
namespace: 'fooNameSpace'
}
};
mockDomainObject = {
type: 'notebook',
name: 'fooRabbitNotebook',
location: 'fooNameSpace:someParent',
identifier: {
key: 'some-object',
namespace: 'fooNameSpace'
@ -68,6 +79,8 @@ describe("The Annotation API", () => {
return mockDomainObject;
} else if (identifier.key === mockAnnotationObject.identifier.key) {
return mockAnnotationObject;
} else if (identifier.key === mockFolderObject.identifier.key) {
return mockFolderObject;
} else {
return null;
}
@ -150,6 +163,7 @@ describe("The Annotation API", () => {
// use local worker
sharedWorkerToRestore = openmct.objects.inMemorySearchProvider.worker;
openmct.objects.inMemorySearchProvider.worker = null;
await openmct.objects.inMemorySearchProvider.index(mockFolderObject);
await openmct.objects.inMemorySearchProvider.index(mockDomainObject);
await openmct.objects.inMemorySearchProvider.index(mockAnnotationObject);
});

View File

@ -34,11 +34,11 @@ import InMemorySearchProvider from './InMemorySearchProvider';
* Uniquely identifies a domain object.
*
* @typedef Identifier
* @memberof module:openmct.ObjectAPI~
* @property {string} namespace the namespace to/from which this domain
* object should be loaded/stored.
* @property {string} key a unique identifier for the domain object
* within that namespace
* @memberof module:openmct.ObjectAPI~
*/
/**
@ -615,27 +615,60 @@ export default class ObjectAPI {
* @param {module:openmct.ObjectAPI~Identifier[]} identifiers
*/
areIdsEqual(...identifiers) {
const firstIdentifier = utils.parseKeyString(identifiers[0]);
return identifiers.map(utils.parseKeyString)
.every(identifier => {
return identifier === identifiers[0]
|| (identifier.namespace === identifiers[0].namespace
&& identifier.key === identifiers[0].key);
return identifier === firstIdentifier
|| (identifier.namespace === firstIdentifier.namespace
&& identifier.key === firstIdentifier.key);
});
}
getOriginalPath(identifier, path = []) {
return this.get(identifier).then((domainObject) => {
path.push(domainObject);
let location = domainObject.location;
/**
* Given an original path check if the path is reachable via root
* @param {Array<Object>} originalPath an array of path objects to check
* @returns {boolean} whether the domain object is reachable
*/
isReachable(originalPath) {
if (originalPath && originalPath.length) {
return (originalPath[originalPath.length - 1].type === 'root');
}
if (location) {
return this.getOriginalPath(utils.parseKeyString(location), path);
} else {
return path;
}
return false;
}
#pathContainsDomainObject(keyStringToCheck, path) {
if (!keyStringToCheck) {
return false;
}
return path.some(pathElement => {
const identifierToCheck = utils.parseKeyString(keyStringToCheck);
return this.areIdsEqual(identifierToCheck, pathElement.identifier);
});
}
/**
* Given an identifier, constructs the original path by walking up its parents
* @param {module:openmct.ObjectAPI~Identifier} identifier
* @param {Array<module:openmct.DomainObject>} path an array of path objects
* @returns {Promise<Array<module:openmct.DomainObject>>} a promise containing an array of domain objects
*/
async getOriginalPath(identifier, path = []) {
const domainObject = await this.get(identifier);
path.push(domainObject);
const { location } = domainObject;
if (location && (!this.#pathContainsDomainObject(location, path))) {
// if we have a location, and we don't already have this in our constructed path,
// then keep walking up the path
return this.getOriginalPath(utils.parseKeyString(location), path);
} else {
return path;
}
}
isObjectPathToALink(domainObject, objectPath) {
return objectPath !== undefined
&& objectPath.length > 1

View File

@ -377,6 +377,73 @@ describe("The Object API", () => {
});
});
describe("getOriginalPath", () => {
let mockGrandParentObject;
let mockParentObject;
let mockChildObject;
beforeEach(() => {
const mockObjectProvider = jasmine.createSpyObj("mock object provider", [
"create",
"update",
"get"
]);
mockGrandParentObject = {
type: 'folder',
name: 'Grand Parent Folder',
location: 'fooNameSpace:child',
identifier: {
key: 'grandParent',
namespace: 'fooNameSpace'
}
};
mockParentObject = {
type: 'folder',
name: 'Parent Folder',
location: 'fooNameSpace:grandParent',
identifier: {
key: 'parent',
namespace: 'fooNameSpace'
}
};
mockChildObject = {
type: 'folder',
name: 'Child Folder',
location: 'fooNameSpace:parent',
identifier: {
key: 'child',
namespace: 'fooNameSpace'
}
};
// eslint-disable-next-line require-await
mockObjectProvider.get = async (identifier) => {
if (identifier.key === mockGrandParentObject.identifier.key) {
return mockGrandParentObject;
} else if (identifier.key === mockParentObject.identifier.key) {
return mockParentObject;
} else if (identifier.key === mockChildObject.identifier.key) {
return mockChildObject;
} else {
return null;
}
};
openmct.objects.addProvider('fooNameSpace', mockObjectProvider);
mockObjectProvider.create.and.returnValue(Promise.resolve(true));
mockObjectProvider.update.and.returnValue(Promise.resolve(true));
openmct.objects.addProvider('fooNameSpace', mockObjectProvider);
});
it('can construct paths even with cycles', async () => {
const objectPath = await objectAPI.getOriginalPath(mockChildObject.identifier);
expect(objectPath.length).toEqual(3);
});
});
describe("transactions", () => {
beforeEach(() => {
spyOn(openmct.editor, 'isEditing').and.returnValue(true);

View File

@ -91,6 +91,10 @@ define([
* @returns keyString
*/
function makeKeyString(identifier) {
if (!identifier) {
throw new Error("Cannot make key string from null identifier");
}
if (isKeyString(identifier)) {
return identifier;
}