Restrict timestrip composition to time based plots, plans and imagery (#5161)

* Restrict timestrip composition to time based plots, plans and imagery

* Adds unit tests for timeline composition policy

* Addresses review comments
Improves tests

* Reuse test objects

Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
This commit is contained in:
Shefali Joshi 2022-06-21 10:15:23 -07:00 committed by GitHub
parent e5e93f311c
commit 417b225505
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 173 additions and 66 deletions

View File

@ -0,0 +1,70 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
const ALLOWED_TYPES = [
'telemetry.plot.overlay',
'telemetry.plot.stacked',
'plan'
];
const DISALLOWED_TYPES = [
'telemetry.plot.bar-graph',
'telemetry.plot.scatter-plot'
];
export default function TimelineCompositionPolicy(openmct) {
function hasNumericTelemetry(domainObject, metadata) {
const hasTelemetry = openmct.telemetry.isTelemetryObject(domainObject);
if (!hasTelemetry || !metadata) {
return false;
}
return metadata.values().length > 0 && hasDomainAndRange(metadata);
}
function hasDomainAndRange(metadata) {
return (metadata.valuesForHints(['range']).length > 0
&& metadata.valuesForHints(['domain']).length > 0);
}
function hasImageTelemetry(domainObject, metadata) {
if (!metadata) {
return false;
}
return metadata.valuesForHints(['image']).length > 0;
}
return {
allow: function (parent, child) {
if (parent.type === 'time-strip') {
const metadata = openmct.telemetry.getMetadata(child);
if (!DISALLOWED_TYPES.includes(child.type)
&& (hasNumericTelemetry(child, metadata) || hasImageTelemetry(child, metadata) || ALLOWED_TYPES.includes(child.type))) {
return true;
}
return false;
}
return true;
}
};
}

View File

@ -22,6 +22,7 @@
import TimelineViewProvider from './TimelineViewProvider';
import timelineInterceptor from "./timelineInterceptor";
import TimelineCompositionPolicy from "./TimelineCompositionPolicy";
export default function () {
return function install(openmct) {
@ -39,6 +40,8 @@ export default function () {
}
});
timelineInterceptor(openmct);
openmct.composition.addPolicy(new TimelineCompositionPolicy(openmct).allow);
openmct.objectViews.addProvider(new TimelineViewProvider(openmct));
};
}

View File

@ -62,6 +62,34 @@ describe('the plugin', function () {
})
}
};
let timelineObject = {
"composition": [],
configuration: {
useIndependentTime: false,
timeOptions: {
mode: {
key: 'fixed'
},
fixedOffsets: {
start: 10,
end: 11
},
clockOffsets: {
start: -(30 * 60 * 1000),
end: (30 * 60 * 1000)
}
}
},
"name": "Some timestrip",
"type": "time-strip",
"location": "mine",
"modified": 1631005183584,
"persisted": 1631005183502,
"identifier": {
"namespace": "",
"key": "b78e7e23-f2b8-4776-b1f0-3ff778f5c8a9"
}
};
beforeEach((done) => {
mockObjectPath = [
@ -134,28 +162,7 @@ describe('the plugin', function () {
beforeEach(() => {
testViewObject = {
id: "test-object",
identifier: {
key: "test-object",
namespace: ''
},
type: "time-strip",
configuration: {
useIndependentTime: false,
timeOptions: {
mode: {
key: 'fixed'
},
fixedOffsets: {
start: 10,
end: 11
},
clockOffsets: {
start: -(30 * 60 * 1000),
end: (30 * 60 * 1000)
}
}
}
...timelineObject
};
const applicableViews = openmct.objectViews.get(testViewObject, mockObjectPath);
@ -187,15 +194,7 @@ describe('the plugin', function () {
beforeEach(() => {
timelineDomainObject = {
identifier: {
key: 'test-object',
namespace: ''
},
type: 'time-strip',
id: "test-object",
configuration: {
useIndependentTime: false
},
...timelineObject,
composition: [
{
identifier: {
@ -236,27 +235,10 @@ describe('the plugin', function () {
describe('the independent time conductor', () => {
let timelineView;
let testViewObject = {
id: "test-object",
identifier: {
key: "test-object",
namespace: ''
},
type: "time-strip",
...timelineObject,
configuration: {
useIndependentTime: true,
timeOptions: {
mode: {
key: 'local'
},
fixedOffsets: {
start: 10,
end: 11
},
clockOffsets: {
start: -(30 * 60 * 1000),
end: (30 * 60 * 1000)
}
}
...timelineObject.configuration,
useIndependentTime: true
}
};
@ -284,27 +266,15 @@ describe('the plugin', function () {
describe('the independent time conductor - fixed', () => {
let timelineView;
let testViewObject2 = {
...timelineObject,
id: "test-object2",
identifier: {
key: "test-object2",
namespace: ''
},
type: "time-strip",
configuration: {
useIndependentTime: true,
timeOptions: {
mode: {
key: 'fixed'
},
fixedOffsets: {
start: 10,
end: 11
},
clockOffsets: {
start: -(30 * 60 * 1000),
end: (30 * 60 * 1000)
}
}
...timelineObject.configuration,
useIndependentTime: true
}
};
@ -328,4 +298,68 @@ describe('the plugin', function () {
});
});
describe("The timestrip composition policy", () => {
let testObject;
beforeEach(() => {
testObject = {
...timelineObject,
composition: []
};
});
it("allows composition for plots", () => {
const testTelemetryObject = {
identifier: {
namespace: "",
key: "test-object"
},
type: "test-object",
name: "Test Object",
telemetry: {
values: [{
key: "some-key",
name: "Some attribute",
hints: {
domain: 1
}
}, {
key: "some-other-key",
name: "Another attribute",
hints: {
range: 1
}
}]
}
};
const composition = openmct.composition.get(testObject);
expect(() => {
composition.add(testTelemetryObject);
}).not.toThrow();
expect(testObject.composition.length).toBe(1);
});
it("allows composition for plans", () => {
const composition = openmct.composition.get(testObject);
expect(() => {
composition.add(planObject);
}).not.toThrow();
expect(testObject.composition.length).toBe(1);
});
it("disallows composition for non time-based plots", () => {
const barGraphObject = {
identifier: {
namespace: "",
key: "test-object"
},
type: "telemetry.plot.bar-graph",
name: "Test Object"
};
const composition = openmct.composition.get(testObject);
expect(() => {
composition.add(barGraphObject);
}).toThrow();
expect(testObject.composition.length).toBe(0);
});
});
});