From 5a8f1d542e20aeb7c14d44984cea3e5d0cefc6aa Mon Sep 17 00:00:00 2001 From: Scott Bell Date: Wed, 8 Feb 2023 15:48:03 +0100 Subject: [PATCH] Allow tags files to define namespace to save annotations (#6274) * allow tags files to define namespace to save annotations * add tests * typo in test name * lint * change param to objects object and remove debug --- example/exampleTags/plugin.js | 14 +++- src/api/annotation/AnnotationAPI.js | 11 ++- src/api/annotation/AnnotationAPISpec.js | 68 +++++++++++++++++++ .../annotations/AnnotationsInspectorView.vue | 1 - src/ui/router/ApplicationRouterSpec.js | 11 ++- 5 files changed, 96 insertions(+), 9 deletions(-) diff --git a/example/exampleTags/plugin.js b/example/exampleTags/plugin.js index b78ad89eb1..ab89d21f0a 100644 --- a/example/exampleTags/plugin.js +++ b/example/exampleTags/plugin.js @@ -20,11 +20,23 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ import availableTags from './tags.json'; + /** +@typedef {{ + namespaceToSaveAnnotations: string +}} TagsPluginOptions +*/ + +/** + * @typedef {TagsPluginOptions} options * @returns {function} The plugin install function */ -export default function exampleTagsPlugin() { +export default function exampleTagsPlugin(options) { return function install(openmct) { + if (options?.namespaceToSaveAnnotations) { + openmct.annotation.setNamespaceToSaveAnnotations(options?.namespaceToSaveAnnotations); + } + Object.keys(availableTags.tags).forEach(tagKey => { const tagDefinition = availableTags.tags[tagKey]; openmct.annotation.defineTag(tagKey, tagDefinition); diff --git a/src/api/annotation/AnnotationAPI.js b/src/api/annotation/AnnotationAPI.js index e9f1350369..47918bf1c2 100644 --- a/src/api/annotation/AnnotationAPI.js +++ b/src/api/annotation/AnnotationAPI.js @@ -84,6 +84,7 @@ export default class AnnotationAPI extends EventEmitter { super(); this.openmct = openmct; this.availableTags = {}; + this.namespaceToSaveAnnotations = ''; this.ANNOTATION_TYPES = ANNOTATION_TYPES; this.ANNOTATION_TYPE = ANNOTATION_TYPE; @@ -139,7 +140,7 @@ export default class AnnotationAPI extends EventEmitter { const domainObjectKeyString = this.openmct.objects.makeKeyString(domainObject.identifier); const originalPathObjects = await this.openmct.objects.getOriginalPath(domainObjectKeyString); const originalContextPath = this.openmct.objects.getRelativePath(originalPathObjects); - const namespace = domainObject.identifier.namespace; + const namespace = this.namespaceToSaveAnnotations; const type = 'annotation'; const typeDefinition = this.openmct.types.get(type); const definition = typeDefinition.definition; @@ -198,6 +199,14 @@ export default class AnnotationAPI extends EventEmitter { this.availableTags[tagKey] = tagsDefinition; } + /** + * @method setNamespaceToSaveAnnotations + * @param {String} namespace the namespace to save new annotations to + */ + setNamespaceToSaveAnnotations(namespace) { + this.namespaceToSaveAnnotations = namespace; + } + /** * @method isAnnotation * @param {DomainObject} domainObject the domainObject in question diff --git a/src/api/annotation/AnnotationAPISpec.js b/src/api/annotation/AnnotationAPISpec.js index a1e6c76984..c4c59d9b9d 100644 --- a/src/api/annotation/AnnotationAPISpec.js +++ b/src/api/annotation/AnnotationAPISpec.js @@ -26,6 +26,7 @@ import ExampleTagsPlugin from "../../../example/exampleTags/plugin"; describe("The Annotation API", () => { let openmct; let mockObjectProvider; + let mockImmutableObjectProvider; let mockDomainObject; let mockFolderObject; let mockAnnotationObject; @@ -89,6 +90,23 @@ describe("The Annotation API", () => { mockObjectProvider.create.and.returnValue(Promise.resolve(true)); mockObjectProvider.update.and.returnValue(Promise.resolve(true)); + mockImmutableObjectProvider = jasmine.createSpyObj("mock immutable provider", [ + "get" + ]); + // eslint-disable-next-line require-await + mockImmutableObjectProvider.get = async (identifier) => { + if (identifier.key === mockDomainObject.identifier.key) { + return mockDomainObject; + } else if (identifier.key === mockAnnotationObject.identifier.key) { + return mockAnnotationObject; + } else if (identifier.key === mockFolderObject.identifier.key) { + return mockFolderObject; + } else { + return null; + } + }; + + openmct.objects.addProvider('immutableProvider', mockImmutableObjectProvider); openmct.objects.addProvider('fooNameSpace', mockObjectProvider); openmct.on('start', done); openmct.startHeadless(); @@ -115,6 +133,22 @@ describe("The Annotation API", () => { expect(annotationObject).toBeDefined(); expect(annotationObject.type).toEqual('annotation'); }); + it("can create annotations if domain object is immutable", async () => { + mockDomainObject.identifier.namespace = 'immutableProvider'; + const annotationCreationArguments = { + name: 'Test Annotation', + domainObject: mockDomainObject, + annotationType: openmct.annotation.ANNOTATION_TYPES.NOTEBOOK, + tags: ['sometag'], + contentText: "fooContext", + targetDomainObjects: [mockDomainObject], + targets: {'fooTarget': {}} + }; + openmct.annotation.setNamespaceToSaveAnnotations('fooNameSpace'); + const annotationObject = await openmct.annotation.create(annotationCreationArguments); + expect(annotationObject).toBeDefined(); + expect(annotationObject.type).toEqual('annotation'); + }); it("fails if annotation is an unknown type", async () => { try { await openmct.annotation.create('Garbage Annotation', mockDomainObject, 'garbageAnnotation', ['sometag'], "fooContext", {'fooTarget': {}}); @@ -122,6 +156,40 @@ describe("The Annotation API", () => { expect(error).toBeDefined(); } }); + it("fails if annotation if given an immutable namespace to save to", async () => { + try { + const annotationCreationArguments = { + name: 'Test Annotation', + domainObject: mockDomainObject, + annotationType: openmct.annotation.ANNOTATION_TYPES.NOTEBOOK, + tags: ['sometag'], + contentText: "fooContext", + targetDomainObjects: [mockDomainObject], + targets: {'fooTarget': {}} + }; + openmct.annotation.setNamespaceToSaveAnnotations('nameespaceThatDoesNotExist'); + await openmct.annotation.create(annotationCreationArguments); + } catch (error) { + expect(error).toBeDefined(); + } + }); + it("fails if annotation if given an undefined namespace to save to", async () => { + try { + const annotationCreationArguments = { + name: 'Test Annotation', + domainObject: mockDomainObject, + annotationType: openmct.annotation.ANNOTATION_TYPES.NOTEBOOK, + tags: ['sometag'], + contentText: "fooContext", + targetDomainObjects: [mockDomainObject], + targets: {'fooTarget': {}} + }; + openmct.annotation.setNamespaceToSaveAnnotations('immutableProvider'); + await openmct.annotation.create(annotationCreationArguments); + } catch (error) { + expect(error).toBeDefined(); + } + }); }); describe("Tagging", () => { diff --git a/src/ui/inspector/annotations/AnnotationsInspectorView.vue b/src/ui/inspector/annotations/AnnotationsInspectorView.vue index db9dc17148..dc1dc07eac 100644 --- a/src/ui/inspector/annotations/AnnotationsInspectorView.vue +++ b/src/ui/inspector/annotations/AnnotationsInspectorView.vue @@ -201,7 +201,6 @@ export default { } }, async loadAnnotationForTargetObject(target) { - console.debug(`📝 Loading annotations for target`, target); const targetID = this.openmct.objects.makeKeyString(target.identifier); const allAnnotationsForTarget = await this.openmct.annotation.getAnnotations(target.identifier); const filteredAnnotationsForSelection = allAnnotationsForTarget.filter(annotation => { diff --git a/src/ui/router/ApplicationRouterSpec.js b/src/ui/router/ApplicationRouterSpec.js index 4e6ca7ac3a..ed4ebd8985 100644 --- a/src/ui/router/ApplicationRouterSpec.js +++ b/src/ui/router/ApplicationRouterSpec.js @@ -21,10 +21,9 @@ describe('Application router utility functions', () => { openmct.on('start', () => { resolveFunction = () => { - const success = window.location.hash !== null && window.location.hash !== ''; - if (success) { - done(); - } + expect(window.location.hash).not.toBe(null); + expect(window.location.hash).not.toBe(''); + done(); }; openmct.router.on('change:hash', resolveFunction); @@ -32,6 +31,7 @@ describe('Application router utility functions', () => { // the above resolve function sometimes doesn't fire due to a race condition. openmct.router.setHash.flush(); openmct.router.setLocationFromUrl(); + openmct.router.setHash.flush(); }); openmct.start(appHolder); @@ -47,8 +47,7 @@ describe('Application router utility functions', () => { }); it('has initial hash when loaded', () => { - const success = window.location.hash !== null; - expect(success).toBe(true); + expect(window.location.hash).not.toBe(null); }); it('The setSearchParam function sets an individual search parameter in the window location hash', () => {