diff options
author | Deep Tailor <deep.j.tailor@nasa.gov> | 2020-12-02 01:42:35 +0300 |
---|---|---|
committer | Deep Tailor <deep.j.tailor@nasa.gov> | 2020-12-02 01:42:35 +0300 |
commit | cb8e906f930dedd443bd13aa1719ddd7b8250847 (patch) | |
tree | 6833c9b4545993827f5cc915ff435229e6cac9c0 | |
parent | be746db7749c4b140d1c510f60e77c67b5e3fd2c (diff) | |
parent | 1c2b0678bebeadb2fd5e84b9554bf57eef99acc1 (diff) |
Merge branch 'master' of https://github.com/nasa/openmct into regex-search-tablesv1.4.0-rc6
-rw-r--r-- | src/MCT.js | 1 | ||||
-rw-r--r-- | src/api/objects/InterceptorRegistry.js | 66 | ||||
-rw-r--r-- | src/api/objects/InterceptorRegistrySpec.js | 0 | ||||
-rw-r--r-- | src/api/objects/ObjectAPI.js | 28 | ||||
-rw-r--r-- | src/api/objects/ObjectAPISpec.js | 48 | ||||
-rw-r--r-- | src/plugins/duplicate/DuplicateAction.js | 5 | ||||
-rw-r--r-- | src/plugins/interceptors/missingObjectInterceptor.js | 40 | ||||
-rw-r--r-- | src/plugins/interceptors/myItemsInterceptor.js | 43 | ||||
-rw-r--r-- | src/plugins/interceptors/plugin.js | 9 | ||||
-rw-r--r-- | src/plugins/move/MoveAction.js | 3 | ||||
-rw-r--r-- | src/plugins/persistence/couch/CouchObjectProvider.js | 3 | ||||
-rw-r--r-- | src/plugins/plot/src/services/ExportImageService.js | 14 | ||||
-rw-r--r-- | src/plugins/plugins.js | 7 | ||||
-rw-r--r-- | src/plugins/telemetryTable/ViewActions.js | 6 | ||||
-rw-r--r-- | src/plugins/telemetryTable/components/table.vue | 2 | ||||
-rw-r--r-- | src/ui/components/object-frame.scss | 2 |
16 files changed, 266 insertions, 11 deletions
diff --git a/src/MCT.js b/src/MCT.js index a5d6ce88b..8e8f8ee21 100644 --- a/src/MCT.js +++ b/src/MCT.js @@ -282,6 +282,7 @@ define([ this.install(this.plugins.NotificationIndicator()); this.install(this.plugins.NewFolderAction()); this.install(this.plugins.ViewDatumAction()); + this.install(this.plugins.ObjectInterceptors()); } MCT.prototype = Object.create(EventEmitter.prototype); diff --git a/src/api/objects/InterceptorRegistry.js b/src/api/objects/InterceptorRegistry.js new file mode 100644 index 000000000..f8a44326d --- /dev/null +++ b/src/api/objects/InterceptorRegistry.js @@ -0,0 +1,66 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2020, 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. + *****************************************************************************/ +export default class InterceptorRegistry { + /** + * A InterceptorRegistry maintains the definitions for different interceptors that may be invoked on domain objects. + * @interface InterceptorRegistry + * @memberof module:openmct + */ + constructor() { + this.interceptors = []; + } + + /** + * @interface InterceptorDef + * @property {function} appliesTo function that determines if this interceptor should be called for the given identifier/object + * @property {function} invoke function that transforms the provided domain object and returns the transformed domain object + * @property {function} priority the priority for this interceptor. A higher number returned has more weight than a lower number + * @memberof module:openmct InterceptorRegistry# + */ + + /** + * Register a new object interceptor. + * + * @param {module:openmct.InterceptorDef} interceptorDef the interceptor to add + * @method addInterceptor + * @memberof module:openmct.InterceptorRegistry# + */ + addInterceptor(interceptorDef) { + //TODO: sort by priority + this.interceptors.push(interceptorDef); + } + + /** + * Retrieve all interceptors applicable to a domain object. + * @method getInterceptors + * @returns [module:openmct.InterceptorDef] the registered interceptors for this identifier/object + * @memberof module:openmct.InterceptorRegistry# + */ + getInterceptors(identifier, object) { + return this.interceptors.filter(interceptor => { + return typeof interceptor.appliesTo === 'function' + && interceptor.appliesTo(identifier, object); + }); + } + +} + diff --git a/src/api/objects/InterceptorRegistrySpec.js b/src/api/objects/InterceptorRegistrySpec.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/api/objects/InterceptorRegistrySpec.js diff --git a/src/api/objects/ObjectAPI.js b/src/api/objects/ObjectAPI.js index 7fb21d89a..c2747be61 100644 --- a/src/api/objects/ObjectAPI.js +++ b/src/api/objects/ObjectAPI.js @@ -26,6 +26,7 @@ define([ './MutableObject', './RootRegistry', './RootObjectProvider', + './InterceptorRegistry', 'EventEmitter' ], function ( _, @@ -33,6 +34,7 @@ define([ MutableObject, RootRegistry, RootObjectProvider, + InterceptorRegistry, EventEmitter ) { @@ -48,6 +50,7 @@ define([ this.rootRegistry = new RootRegistry(); this.rootProvider = new RootObjectProvider.default(this.rootRegistry); this.cache = {}; + this.interceptorRegistry = new InterceptorRegistry.default(); } /** @@ -177,6 +180,10 @@ define([ return objectPromise.then(result => { delete this.cache[keystring]; + const interceptors = this.listGetInterceptors(identifier, result); + interceptors.forEach(interceptor => { + result = interceptor.invoke(identifier, result); + }); return result; }); @@ -313,6 +320,27 @@ define([ }; /** + * Register an object interceptor that transforms a domain object requested via module:openmct.ObjectAPI.get + * The domain object will be transformed after it is retrieved from the persistence store + * The domain object will be transformed only if the interceptor is applicable to that domain object as defined by the InterceptorDef + * + * @param {module:openmct.InterceptorDef} interceptorDef the interceptor definition to add + * @method addGetInterceptor + * @memberof module:openmct.InterceptorRegistry# + */ + ObjectAPI.prototype.addGetInterceptor = function (interceptorDef) { + this.interceptorRegistry.addInterceptor(interceptorDef); + }; + + /** + * Retrieve the interceptors for a given domain object. + * @private + */ + ObjectAPI.prototype.listGetInterceptors = function (identifier, object) { + return this.interceptorRegistry.getInterceptors(identifier, object); + }; + + /** * Uniquely identifies a domain object. * * @typedef Identifier diff --git a/src/api/objects/ObjectAPISpec.js b/src/api/objects/ObjectAPISpec.js index 22c037a50..ad6293b72 100644 --- a/src/api/objects/ObjectAPISpec.js +++ b/src/api/objects/ObjectAPISpec.js @@ -63,12 +63,51 @@ describe("The Object API", () => { describe("The get function", () => { describe("when a provider is available", () => { let mockProvider; + let mockInterceptor; + let anotherMockInterceptor; + let notApplicableMockInterceptor; beforeEach(() => { mockProvider = jasmine.createSpyObj("mock provider", [ "get" ]); mockProvider.get.and.returnValue(Promise.resolve(mockDomainObject)); + + mockInterceptor = jasmine.createSpyObj("mock interceptor", [ + "appliesTo", + "invoke" + ]); + mockInterceptor.appliesTo.and.returnValue(true); + mockInterceptor.invoke.and.callFake((identifier, object) => { + return Object.assign({ + changed: true + }, object); + }); + + anotherMockInterceptor = jasmine.createSpyObj("another mock interceptor", [ + "appliesTo", + "invoke" + ]); + anotherMockInterceptor.appliesTo.and.returnValue(true); + anotherMockInterceptor.invoke.and.callFake((identifier, object) => { + return Object.assign({ + alsoChanged: true + }, object); + }); + + notApplicableMockInterceptor = jasmine.createSpyObj("not applicable mock interceptor", [ + "appliesTo", + "invoke" + ]); + notApplicableMockInterceptor.appliesTo.and.returnValue(false); + notApplicableMockInterceptor.invoke.and.callFake((identifier, object) => { + return Object.assign({ + shouldNotBeChanged: true + }, object); + }); objectAPI.addProvider(TEST_NAMESPACE, mockProvider); + objectAPI.addGetInterceptor(mockInterceptor); + objectAPI.addGetInterceptor(anotherMockInterceptor); + objectAPI.addGetInterceptor(notApplicableMockInterceptor); }); it("Caches multiple requests for the same object", () => { @@ -78,6 +117,15 @@ describe("The Object API", () => { objectAPI.get(mockDomainObject.identifier); expect(mockProvider.get.calls.count()).toBe(1); }); + + it("applies any applicable interceptors", () => { + expect(mockDomainObject.changed).toBeUndefined(); + objectAPI.get(mockDomainObject.identifier).then((object) => { + expect(object.changed).toBeTrue(); + expect(object.alsoChanged).toBeTrue(); + expect(object.shouldNotBeChanged).toBeUndefined(); + }); + }); }); }); }); diff --git a/src/plugins/duplicate/DuplicateAction.js b/src/plugins/duplicate/DuplicateAction.js index e398b6c04..57d7e7f33 100644 --- a/src/plugins/duplicate/DuplicateAction.js +++ b/src/plugins/duplicate/DuplicateAction.js @@ -143,13 +143,16 @@ export default class DuplicateAction { let parent = objectPath[1]; let parentType = parent && this.openmct.types.get(parent.type); let child = objectPath[0]; + let childType = child && this.openmct.types.get(child.type); let locked = child.locked ? child.locked : parent && parent.locked; if (locked) { return false; } - return parentType + return childType + && childType.definition.creatable + && parentType && parentType.definition.creatable && Array.isArray(parent.composition); } diff --git a/src/plugins/interceptors/missingObjectInterceptor.js b/src/plugins/interceptors/missingObjectInterceptor.js new file mode 100644 index 000000000..029b65bf4 --- /dev/null +++ b/src/plugins/interceptors/missingObjectInterceptor.js @@ -0,0 +1,40 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2020, 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. + *****************************************************************************/ + +export default function MissingObjectInterceptor(openmct) { + openmct.objects.addGetInterceptor({ + appliesTo: (identifier, domainObject) => { + return identifier.key !== 'mine'; + }, + invoke: (identifier, object) => { + if (object === undefined) { + return { + identifier, + type: 'unknown', + name: 'Missing: ' + openmct.objects.makeKeyString(identifier) + }; + } + + return object; + } + }); +} diff --git a/src/plugins/interceptors/myItemsInterceptor.js b/src/plugins/interceptors/myItemsInterceptor.js new file mode 100644 index 000000000..ef92839e5 --- /dev/null +++ b/src/plugins/interceptors/myItemsInterceptor.js @@ -0,0 +1,43 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2020, 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. + *****************************************************************************/ + +export default function MyItemsInterceptor(openmct) { + + openmct.objects.addGetInterceptor({ + appliesTo: (identifier, domainObject) => { + return identifier.key === 'mine'; + }, + invoke: (identifier, object) => { + if (object === undefined) { + return { + identifier, + "name": "My Items", + "type": "folder", + "composition": [], + "location": "ROOT" + }; + } + + return object; + } + }); +} diff --git a/src/plugins/interceptors/plugin.js b/src/plugins/interceptors/plugin.js new file mode 100644 index 000000000..446010094 --- /dev/null +++ b/src/plugins/interceptors/plugin.js @@ -0,0 +1,9 @@ +import missingObjectInterceptor from "./missingObjectInterceptor"; +import myItemsInterceptor from "./myItemsInterceptor"; + +export default function plugin() { + return function install(openmct) { + myItemsInterceptor(openmct); + missingObjectInterceptor(openmct); + }; +} diff --git a/src/plugins/move/MoveAction.js b/src/plugins/move/MoveAction.js index 2a4605307..8b46d109f 100644 --- a/src/plugins/move/MoveAction.js +++ b/src/plugins/move/MoveAction.js @@ -154,6 +154,7 @@ export default class MoveAction { let parent = objectPath[1]; let parentType = parent && this.openmct.types.get(parent.type); let child = objectPath[0]; + let childType = child && this.openmct.types.get(child.type); if (child.locked || (parent && parent.locked)) { return false; @@ -161,6 +162,8 @@ export default class MoveAction { return parentType && parentType.definition.creatable + && childType + && childType.definition.creatable && Array.isArray(parent.composition); } } diff --git a/src/plugins/persistence/couch/CouchObjectProvider.js b/src/plugins/persistence/couch/CouchObjectProvider.js index 1df673eb7..8c7c4d3bf 100644 --- a/src/plugins/persistence/couch/CouchObjectProvider.js +++ b/src/plugins/persistence/couch/CouchObjectProvider.js @@ -87,7 +87,8 @@ export default class CouchObjectProvider { } //Sometimes CouchDB returns the old rev which fetching the object if there is a document update in progress - if (!this.objectQueue[key].pending) { + //Only update the rev if it's the first time we're getting the object from CouchDB. Subsequent revs should only be updated by updates. + if (!this.objectQueue[key].pending && !this.objectQueue[key].rev) { this.objectQueue[key].updateRevision(response[REV]); } diff --git a/src/plugins/plot/src/services/ExportImageService.js b/src/plugins/plot/src/services/ExportImageService.js index 74967a577..7373b6a9e 100644 --- a/src/plugins/plot/src/services/ExportImageService.js +++ b/src/plugins/plot/src/services/ExportImageService.js @@ -117,8 +117,10 @@ define( * @returns {promise} */ ExportImageService.prototype.exportJPG = function (element, filename, className) { + const processedFilename = replaceDotsWithUnderscores(filename); + return this.renderElement(element, "jpg", className).then(function (img) { - saveAs(img, filename); + saveAs(img, processedFilename); }); }; @@ -130,8 +132,10 @@ define( * @returns {promise} */ ExportImageService.prototype.exportPNG = function (element, filename, className) { + const processedFilename = replaceDotsWithUnderscores(filename); + return this.renderElement(element, "png", className).then(function (img) { - saveAs(img, filename); + saveAs(img, processedFilename); }); }; @@ -146,6 +150,12 @@ define( return this.renderElement(element, "png", className); }; + function replaceDotsWithUnderscores(filename) { + const regex = /\./gi; + + return filename.replace(regex, '_'); + } + /** * canvas.toBlob() not supported in IE < 10, Opera, and Safari. This polyfill * implements the method in browsers that would not otherwise support it. diff --git a/src/plugins/plugins.js b/src/plugins/plugins.js index 03be9ec6c..875918f8d 100644 --- a/src/plugins/plugins.js +++ b/src/plugins/plugins.js @@ -59,7 +59,8 @@ define([ './persistence/couch/plugin', './defaultRootName/plugin', './timeline/plugin', - './viewDatumAction/plugin' + './viewDatumAction/plugin', + './interceptors/plugin' ], function ( _, UTCTimeSystem, @@ -99,7 +100,8 @@ define([ CouchDBPlugin, DefaultRootName, Timeline, - ViewDatumAction + ViewDatumAction, + ObjectInterceptors ) { const bundleMap = { LocalStorage: 'platform/persistence/local', @@ -194,6 +196,7 @@ define([ plugins.DefaultRootName = DefaultRootName.default; plugins.Timeline = Timeline.default; plugins.ViewDatumAction = ViewDatumAction.default; + plugins.ObjectInterceptors = ObjectInterceptors.default; return plugins; }); diff --git a/src/plugins/telemetryTable/ViewActions.js b/src/plugins/telemetryTable/ViewActions.js index d4d8212ca..8d438fc5d 100644 --- a/src/plugins/telemetryTable/ViewActions.js +++ b/src/plugins/telemetryTable/ViewActions.js @@ -30,13 +30,13 @@ let exportCSV = { }, group: 'view' }; -let exportMarkedRows = { +let exportMarkedDataAsCSV = { name: 'Export Marked Rows', key: 'export-csv-marked', description: "Export marked rows as CSV", cssClass: 'icon-download labeled', invoke: (objectPath, viewProvider) => { - viewProvider.getViewContext().exportMarkedRows(); + viewProvider.getViewContext().exportMarkedDataAsCSV(); }, group: 'view' }; @@ -98,7 +98,7 @@ let autosizeColumns = { let viewActions = [ exportCSV, - exportMarkedRows, + exportMarkedDataAsCSV, unmarkAllRows, pause, play, diff --git a/src/plugins/telemetryTable/components/table.vue b/src/plugins/telemetryTable/components/table.vue index 98abacf6b..515a317e0 100644 --- a/src/plugins/telemetryTable/components/table.vue +++ b/src/plugins/telemetryTable/components/table.vue @@ -992,7 +992,7 @@ export default { return { type: 'telemetry-table', exportAllDataAsCSV: this.exportAllDataAsCSV, - exportMarkedRows: this.exportMarkedRows, + exportMarkedDataAsCSV: this.exportMarkedDataAsCSV, unmarkAllRows: this.unmarkAllRows, togglePauseByButton: this.togglePauseByButton, expandColumns: this.recalculateColumnWidths, diff --git a/src/ui/components/object-frame.scss b/src/ui/components/object-frame.scss index 97df32cf8..4247f7999 100644 --- a/src/ui/components/object-frame.scss +++ b/src/ui/components/object-frame.scss @@ -162,5 +162,5 @@ // This element is the recipient for object styling; cannot be display: contents flex: 1 1 auto; overflow: hidden; - display: contents; + display: block; } |