mirror of
https://github.com/nasa/openmct.git
synced 2025-02-21 01:42:31 +00:00
Add object interceptor API to allow missing model and missing my-items handling (#3522)
* Extends Object API to allow adding interceptors Co-authored-by: Andrew Henry <akhenry@gmail.com>
This commit is contained in:
parent
176226ddef
commit
9da750c3bb
@ -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);
|
||||
|
66
src/api/objects/InterceptorRegistry.js
Normal file
66
src/api/objects/InterceptorRegistry.js
Normal file
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
0
src/api/objects/InterceptorRegistrySpec.js
Normal file
0
src/api/objects/InterceptorRegistrySpec.js
Normal file
@ -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;
|
||||
});
|
||||
@ -312,6 +319,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.
|
||||
*
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
40
src/plugins/interceptors/missingObjectInterceptor.js
Normal file
40
src/plugins/interceptors/missingObjectInterceptor.js
Normal file
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
43
src/plugins/interceptors/myItemsInterceptor.js
Normal file
43
src/plugins/interceptors/myItemsInterceptor.js
Normal file
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
9
src/plugins/interceptors/plugin.js
Normal file
9
src/plugins/interceptors/plugin.js
Normal file
@ -0,0 +1,9 @@
|
||||
import missingObjectInterceptor from "./missingObjectInterceptor";
|
||||
import myItemsInterceptor from "./myItemsInterceptor";
|
||||
|
||||
export default function plugin() {
|
||||
return function install(openmct) {
|
||||
myItemsInterceptor(openmct);
|
||||
missingObjectInterceptor(openmct);
|
||||
};
|
||||
}
|
@ -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]);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user