From f8464fa76fc5a7a9c9b6921bd42c86e771f6b38f Mon Sep 17 00:00:00 2001 From: Joshi Date: Fri, 27 Dec 2019 12:57:30 -0800 Subject: [PATCH 01/10] Adds telemetry criterion class and related tests --- .../condition/criterion/TelemetryCriterion.js | 83 ++++++++++++++ .../criterion/TelemetryCriterionSpec.js | 107 ++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 src/plugins/condition/criterion/TelemetryCriterion.js create mode 100644 src/plugins/condition/criterion/TelemetryCriterionSpec.js diff --git a/src/plugins/condition/criterion/TelemetryCriterion.js b/src/plugins/condition/criterion/TelemetryCriterion.js new file mode 100644 index 0000000000..9f02851a6e --- /dev/null +++ b/src/plugins/condition/criterion/TelemetryCriterion.js @@ -0,0 +1,83 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2019, 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. + *****************************************************************************/ + +import * as EventEmitter from 'eventemitter3'; + +export default class TelemetryCriterion extends EventEmitter { + + constructor(telemetryDomainObject, openmct) { + super(); + + this.telemetryObject = telemetryDomainObject; + this.openmct = openmct; + this.telemetryAPI = this.openmct.telemetry; + this.subscription = null; + this.telemetryObjectIdAsString = null; + this.telemetryMetadata = null; + if (this.telemetryAPI.isTelemetryObject(this.telemetryObject)) { + this.telemetryObjectIdAsString = this.openmct.objects.makeKeyString(this.telemetryObject.identifier); + } + } + + handleSubscription(datum) { + //data is telemetry values, error + //how do I get data here? + this.emitResult(this.normalizeData(datum)); + } + + //TODO: Revisit this logic + normalizeData(datum) { + return { + [datum.key]: datum[datum.source] + } + } + + emitResult(data, error) { + this.emit('criterion::Update', { + identifier: this.telemetryObjectIdAsString, + data: data, + error: error + }); + } + + /** + * Subscribes to the telemetry object and returns an unsubscribe function + */ + subscribe() { + this.subscription = this.telemetryAPI.subscribe(this.telemetryObject, (datum) => { + this.handleSubscription(datum); + }); + } + + /** + * Calls an unsubscribe function returned by subscribe() and deletes any initialized data + */ + unsubscribe() { + //unsubscribe from telemetry source + this.subscription(); + delete this.subscription; + this.emit('criterion::Remove', this.telemetryObjectIdAsString); + delete this.telemetryObjectIdAsString; + delete this.telemetryObject; + delete this.telemetryMetadata; + } +} diff --git a/src/plugins/condition/criterion/TelemetryCriterionSpec.js b/src/plugins/condition/criterion/TelemetryCriterionSpec.js new file mode 100644 index 0000000000..f5b1bc3bc4 --- /dev/null +++ b/src/plugins/condition/criterion/TelemetryCriterionSpec.js @@ -0,0 +1,107 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2019, 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. + *****************************************************************************/ + +import TelemetryCriterion from "./TelemetryCriterion"; +import { createOpenMct } from "../../../testTools"; + +let openmct, + mockListener, + testTelemetryObject, + telemetryCriterion; + +describe("The telemetry criterion", function () { + + beforeEach (() => { + openmct = createOpenMct(); + mockListener = jasmine.createSpy('listener'); + 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 + } + }] + } + }; + + telemetryCriterion = new TelemetryCriterion( + testTelemetryObject, + openmct + ); + + telemetryCriterion.on('criterion::Update', mockListener); + + }); + + it("initializes with a telemetry objectId as string", function () { + expect(telemetryCriterion.telemetryObjectIdAsString).toEqual(testTelemetryObject.identifier.key); + }); + + it("subscribes to telemetry providers", function () { + telemetryCriterion.subscribe(); + expect(telemetryCriterion.subscription).toBeDefined(); + }); + + it("normalizes telemetry data", function () { + let result = telemetryCriterion.normalizeData({ + key: 'some-key', + source: 'testSource', + testSource: 'Hello' + }); + expect(result).toEqual({ + 'some-key': 'Hello' + }) + }); + + it("emits update event on new data from telemetry providers", function () { + spyOn(telemetryCriterion, 'emitResult').and.callThrough(); + telemetryCriterion.handleSubscription({ + key: 'some-key', + source: 'testSource', + testSource: 'Hello' + }); + expect(telemetryCriterion.emitResult).toHaveBeenCalled(); + expect(mockListener).toHaveBeenCalled(); + }); + + it("un-subscribes from telemetry providers", function () { + telemetryCriterion.subscribe(); + expect(telemetryCriterion.subscription).toBeDefined(); + telemetryCriterion.unsubscribe(); + expect(telemetryCriterion.subscription).toBeUndefined(); + expect(telemetryCriterion.telemetryObjectIdAsString).toBeUndefined(); + expect(telemetryCriterion.telemetryObject).toBeUndefined(); + expect(telemetryCriterion.telemetryMetadata).toBeUndefined(); + }); + +}); From 8c796b4e5708e28dfaf3fa2796d796d6d739803b Mon Sep 17 00:00:00 2001 From: Joshi Date: Mon, 6 Jan 2020 10:09:47 -0800 Subject: [PATCH 02/10] Allows adding new conditions and associated criteria. Stub for evaluating conditions. --- src/plugins/condition/Condition.js | 178 +++++++++++++++++++++++ src/plugins/condition/utils/constants.js | 4 + src/plugins/condition/utils/evaluator.js | 7 + 3 files changed, 189 insertions(+) create mode 100644 src/plugins/condition/Condition.js create mode 100644 src/plugins/condition/utils/constants.js create mode 100644 src/plugins/condition/utils/evaluator.js diff --git a/src/plugins/condition/Condition.js b/src/plugins/condition/Condition.js new file mode 100644 index 0000000000..94f4b98c68 --- /dev/null +++ b/src/plugins/condition/Condition.js @@ -0,0 +1,178 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2019, 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. + *****************************************************************************/ + +import * as EventEmitter from 'eventemitter3'; +import UUID from 'uuid'; +import TelemetryCriterion from "@/plugins/condition/criterion/TelemetryCriterion"; +import {computeConditionForAll, computeConditionForAny} from 'utils/evaluator' +import { TRIGGER } from "@/plugins/condition/utils/constants"; + +/* +* conditionDefinition = { +* trigger: 'any'/'all', +* criteria: [ +* { +* object: { +* operator: '', +* input: '', +* metaDataKey: '', +* telemetryObjectKey: 'someTelemetryObjectKey' +* } +* } +* ] +* } +*/ +export default class Condition extends EventEmitter { + + constructor(conditionDefinition, openmct) { + super(); + + this.openmct = openmct; + this.id = new UUID(); + this.criteriaMap = conditionDefinition.criteria ? this.createCriteria(conditionDefinition.criteria) : {}; + this.trigger = conditionDefinition.trigger; + this.result = null; + } + + updateTrigger(conditionDefinition) { + if (this.trigger !== conditionDefinition.trigger) { + this.trigger = conditionDefinition.trigger; + this.handleConditionUpdated(); + } + } + + generateNewCriterion() { + return { + id: new UUID(), + object: '', + key: '', + operation: '', + values: [] + }; + } + + createCriteria(criterionDefinitions) { + criterionDefinitions.forEach((criterionDefinition) => { + this.addCriterion(criterionDefinition); + }); + } + + updateCriteria(criterionDefinitions) { + this.destroyCriteria(); + this.createCriteria(criterionDefinitions); + } + + /** + * adds criterion to the condition. + */ + addCriterion(criterionDefinition) { + if (!criterionDefinition) { + criterionDefinition = this.generateNewCriterion(); + } + let criterion = new TelemetryCriterion(criterionDefinition.object, this.openmct); + criterion.on('criterion::Update', this.handleCriterionUpdated); + this.criteriaMap[criterionDefinition.id] = criterion; + this.handleConditionUpdated(); + return criterionDefinition.id; + } + + findCriterion(id) { + return this.criteriaMap[id] || null; + } + + updateCriterion(id, criterionDefinition) { + if (this.destroyCriterion(id)) { + this.criteriaMap[id] = new TelemetryCriterion(criterionDefinition.object, this.openmct); + this.handleConditionUpdated(); + } + } + + removeCriterion(id) { + if (this.destroyCriterion(id)) { + this.handleConditionUpdated(); + } + } + + destroyCriterion(id) { + let criterion = this.findCriterion(id); + const criterionId = id; + if (criterion) { + criterion.unsubscribe(); + criterion.off('criterion::Update', (result) => { + this.handleCriterionUpdated(criterionId, result); + }); + delete this.criteriaMap[id]; + return true; + } + return false; + } + + handleCriterionUpdated(id, result) { + // reevaluate the condition's output + // TODO: should we save the result of a criterion here or in the criterion object itself? + this.evaluate(); + this.handleConditionUpdated(); + } + + handleConditionUpdated() { + // trigger an updated event so that consumers can react accordingly + } + + getCriteria() { + let criteria = []; + for(let id in this.criteriaMap) { + if (this.criteriaMap.hasOwnProperty(id) && this.criteriaMap[id]) { + criteria.push(this.criteriaMap[id]); + } + } + + return criteria; + } + + destroyCriteria() { + let success = true; + for(let id in this.criteriaMap) { + if (this.criteriaMap.hasOwnProperty(id) && this.criteriaMap[id]) { + success = success && this.destroyCriterion(this.criteriaMap[id]); + } + } + + return success; + } + + evaluate() { + let criteria = this.getCriteria(); + if (this.trigger === TRIGGER.ANY) { + this.result = computeConditionForAny(criteria); + } else if (this.trigger === TRIGGER.ALL) { + this.result = computeConditionForAll(criteria); + } + } + + emitResult(data, error) { + this.emit('condition::Update', { + identifier: this.id, + data: data, + error: error + }); + } +} diff --git a/src/plugins/condition/utils/constants.js b/src/plugins/condition/utils/constants.js new file mode 100644 index 0000000000..968c6168bf --- /dev/null +++ b/src/plugins/condition/utils/constants.js @@ -0,0 +1,4 @@ +export const TRIGGER = { + ANY: 'any', + ALL: 'all' +}; diff --git a/src/plugins/condition/utils/evaluator.js b/src/plugins/condition/utils/evaluator.js new file mode 100644 index 0000000000..51bd7173f6 --- /dev/null +++ b/src/plugins/condition/utils/evaluator.js @@ -0,0 +1,7 @@ +export const computeConditionForAny = (args) => { + return false; +}; + +export const computeConditionForAll = (args) => { + return false; +}; From fe3cc661d31f20a62109ccb1c6eb7ebeef91d830 Mon Sep 17 00:00:00 2001 From: Joshi Date: Wed, 8 Jan 2020 12:20:17 -0800 Subject: [PATCH 03/10] Fixes tests. --- src/plugins/condition/criterion/TelemetryCriterionSpec.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/condition/criterion/TelemetryCriterionSpec.js b/src/plugins/condition/criterion/TelemetryCriterionSpec.js index f5b1bc3bc4..c49cd96bd8 100644 --- a/src/plugins/condition/criterion/TelemetryCriterionSpec.js +++ b/src/plugins/condition/criterion/TelemetryCriterionSpec.js @@ -23,7 +23,7 @@ import TelemetryCriterion from "./TelemetryCriterion"; import { createOpenMct } from "../../../testTools"; -let openmct, +let openmct = createOpenMct(), mockListener, testTelemetryObject, telemetryCriterion; @@ -31,7 +31,6 @@ let openmct, describe("The telemetry criterion", function () { beforeEach (() => { - openmct = createOpenMct(); mockListener = jasmine.createSpy('listener'); testTelemetryObject = { identifier:{ namespace: "", key: "test-object"}, From 36055b7c0469aa28ee5a9df083213a02464b6754 Mon Sep 17 00:00:00 2001 From: Joshi Date: Thu, 9 Jan 2020 06:44:19 -0800 Subject: [PATCH 04/10] Changes name of emitted event --- src/plugins/condition/criterion/TelemetryCriterion.js | 2 +- src/plugins/condition/criterion/TelemetryCriterionSpec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/condition/criterion/TelemetryCriterion.js b/src/plugins/condition/criterion/TelemetryCriterion.js index 9f02851a6e..0637c34269 100644 --- a/src/plugins/condition/criterion/TelemetryCriterion.js +++ b/src/plugins/condition/criterion/TelemetryCriterion.js @@ -52,7 +52,7 @@ export default class TelemetryCriterion extends EventEmitter { } emitResult(data, error) { - this.emit('criterion::Update', { + this.emit('criterionUpdated', { identifier: this.telemetryObjectIdAsString, data: data, error: error diff --git a/src/plugins/condition/criterion/TelemetryCriterionSpec.js b/src/plugins/condition/criterion/TelemetryCriterionSpec.js index c49cd96bd8..302e706dd7 100644 --- a/src/plugins/condition/criterion/TelemetryCriterionSpec.js +++ b/src/plugins/condition/criterion/TelemetryCriterionSpec.js @@ -58,7 +58,7 @@ describe("The telemetry criterion", function () { openmct ); - telemetryCriterion.on('criterion::Update', mockListener); + telemetryCriterion.on('criterionUpdated', mockListener); }); From 91c877f2346a047460ad6c47f26022df2495ee70 Mon Sep 17 00:00:00 2001 From: Joshi Date: Fri, 10 Jan 2020 10:43:55 -0800 Subject: [PATCH 05/10] Adds tests for Condition class --- package.json | 1 + src/plugins/condition/Condition.js | 109 +++++++++++++----------- src/plugins/condition/ConditionSpec.js | 111 +++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 48 deletions(-) create mode 100644 src/plugins/condition/ConditionSpec.js diff --git a/package.json b/package.json index 50a8d53cd9..bd876eb67e 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "request": "^2.69.0", "split": "^1.0.0", "style-loader": "^1.0.1", + "uuid": "^3.3.3", "v8-compile-cache": "^1.1.0", "vue": "2.5.6", "vue-loader": "^15.2.6", diff --git a/src/plugins/condition/Condition.js b/src/plugins/condition/Condition.js index 94f4b98c68..5a5bb3e790 100644 --- a/src/plugins/condition/Condition.js +++ b/src/plugins/condition/Condition.js @@ -21,9 +21,8 @@ *****************************************************************************/ import * as EventEmitter from 'eventemitter3'; -import UUID from 'uuid'; +import uuid from 'uuid'; import TelemetryCriterion from "@/plugins/condition/criterion/TelemetryCriterion"; -import {computeConditionForAll, computeConditionForAny} from 'utils/evaluator' import { TRIGGER } from "@/plugins/condition/utils/constants"; /* @@ -31,12 +30,10 @@ import { TRIGGER } from "@/plugins/condition/utils/constants"; * trigger: 'any'/'all', * criteria: [ * { -* object: { -* operator: '', -* input: '', -* metaDataKey: '', -* telemetryObjectKey: 'someTelemetryObjectKey' -* } +* operation: '', +* input: '', +* metaDataKey: '', +* key: 'someTelemetryObjectKey' * } * ] * } @@ -47,8 +44,12 @@ export default class Condition extends EventEmitter { super(); this.openmct = openmct; - this.id = new UUID(); - this.criteriaMap = conditionDefinition.criteria ? this.createCriteria(conditionDefinition.criteria) : {}; + this.id = uuid(); + if (conditionDefinition.criteria) { + this.createCriteria(conditionDefinition.criteria); + } else { + this.criteria = []; + } this.trigger = conditionDefinition.trigger; this.result = null; } @@ -60,13 +61,13 @@ export default class Condition extends EventEmitter { } } - generateNewCriterion() { + generateCriterion(criterionDefinition) { return { - id: new UUID(), - object: '', - key: '', - operation: '', - values: [] + id: uuid(), + operation: criterionDefinition.operation || '', + input: criterionDefinition.input === undefined ? [] : criterionDefinition.input, + metaDataKey: criterionDefinition.metaDataKey || '', + key: criterionDefinition.key || '' }; } @@ -85,23 +86,43 @@ export default class Condition extends EventEmitter { * adds criterion to the condition. */ addCriterion(criterionDefinition) { - if (!criterionDefinition) { - criterionDefinition = this.generateNewCriterion(); + let criterionDefinitionWithId = this.generateCriterion(criterionDefinition || null); + let criterion = new TelemetryCriterion(criterionDefinitionWithId, this.openmct); + criterion.on('criterionUpdated', this.handleCriterionUpdated); + if (!this.criteria) { + this.criteria = []; } - let criterion = new TelemetryCriterion(criterionDefinition.object, this.openmct); - criterion.on('criterion::Update', this.handleCriterionUpdated); - this.criteriaMap[criterionDefinition.id] = criterion; + this.criteria.push(criterion); this.handleConditionUpdated(); - return criterionDefinition.id; + return criterionDefinitionWithId.id; } findCriterion(id) { - return this.criteriaMap[id] || null; + let criterion; + + for (let i=0, ii=this.criteria.length; i < ii; i ++) { + if (this.criteria[i].id === id) { + criterion = { + item: this.criteria[i], + index: i + } + } + } + + return criterion; } updateCriterion(id, criterionDefinition) { - if (this.destroyCriterion(id)) { - this.criteriaMap[id] = new TelemetryCriterion(criterionDefinition.object, this.openmct); + let found = this.findCriterion(id); + if (found) { + const newCriterionDefinition = this.generateCriterion(criterionDefinition); + let newCriterion = new TelemetryCriterion(newCriterionDefinition, this.openmct); + let criterion = found.item; + criterion.unsubscribe(); + criterion.off('criterionUpdated', (result) => { + this.handleCriterionUpdated(id, result); + }); + this.criteria.splice(found.index, 1, newCriterion); this.handleConditionUpdated(); } } @@ -113,14 +134,14 @@ export default class Condition extends EventEmitter { } destroyCriterion(id) { - let criterion = this.findCriterion(id); - const criterionId = id; - if (criterion) { + let found = this.findCriterion(id); + if (found) { + let criterion = found.item; criterion.unsubscribe(); - criterion.off('criterion::Update', (result) => { - this.handleCriterionUpdated(criterionId, result); + criterion.off('criterionUpdated', (result) => { + this.handleCriterionUpdated(id, result); }); - delete this.criteriaMap[id]; + this.criteria.splice(found.index, 1); return true; } return false; @@ -135,41 +156,33 @@ export default class Condition extends EventEmitter { handleConditionUpdated() { // trigger an updated event so that consumers can react accordingly + this.emitResult(); } getCriteria() { - let criteria = []; - for(let id in this.criteriaMap) { - if (this.criteriaMap.hasOwnProperty(id) && this.criteriaMap[id]) { - criteria.push(this.criteriaMap[id]); - } - } - - return criteria; + return this.criteria; } destroyCriteria() { let success = true; - for(let id in this.criteriaMap) { - if (this.criteriaMap.hasOwnProperty(id) && this.criteriaMap[id]) { - success = success && this.destroyCriterion(this.criteriaMap[id]); - } + //looping through the array backwards since destroyCriterion modifies the criteria array + for (let i=this.criteria.length-1; i >= 0; i--) { + success = success && this.destroyCriterion(this.criteria[i].id); } - return success; } + //TODO: implement as part of the evaluator class task. evaluate() { - let criteria = this.getCriteria(); if (this.trigger === TRIGGER.ANY) { - this.result = computeConditionForAny(criteria); + this.result = false; } else if (this.trigger === TRIGGER.ALL) { - this.result = computeConditionForAll(criteria); + this.result = false; } } emitResult(data, error) { - this.emit('condition::Update', { + this.emit('conditionUpdated', { identifier: this.id, data: data, error: error diff --git a/src/plugins/condition/ConditionSpec.js b/src/plugins/condition/ConditionSpec.js new file mode 100644 index 0000000000..efb19d6fbe --- /dev/null +++ b/src/plugins/condition/ConditionSpec.js @@ -0,0 +1,111 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2019, 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. + *****************************************************************************/ + +import Condition from "./Condition"; +import {TRIGGER} from "./utils/constants"; +import TelemetryCriterion from "./criterion/TelemetryCriterion"; + +let openmct = {}, + mockListener, + testConditionDefinition, + testTelemetryObject, + conditionObj; + +describe("The condition", function () { + + beforeEach (() => { + mockListener = jasmine.createSpy('listener'); + 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 + } + }] + } + }; + openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString']); + openmct.objects.get.and.returnValue(testTelemetryObject); + openmct.objects.makeKeyString.and.returnValue(testTelemetryObject.identifier.key); + openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject']); + openmct.telemetry.isTelemetryObject.and.returnValue(true); + + testConditionDefinition = { + trigger: TRIGGER.ANY, + criteria: [ + { + operation: 'equalTo', + input: false, + metaDataKey: 'value', + key: testTelemetryObject.identifier + } + ] + }; + + conditionObj = new Condition( + testConditionDefinition, + openmct + ); + + conditionObj.on('conditionUpdated', mockListener); + + }); + + it("generates criteria with an id", function () { + const testCriterion = testConditionDefinition.criteria[0]; + let criterion = conditionObj.generateCriterion(testCriterion); + expect(criterion.id).toBeDefined(); + expect(criterion.operation).toEqual(testCriterion.operation); + expect(criterion.input).toEqual(testCriterion.input); + expect(criterion.metaDataKey).toEqual(testCriterion.metaDataKey); + expect(criterion.key).toEqual(testCriterion.key); + }); + + it("initializes with an id", function () { + expect(conditionObj.id).toBeDefined(); + }); + + it("initializes with criteria from the condition definition", function () { + expect(conditionObj.criteria.length).toEqual(1); + let criterion = conditionObj.criteria[0]; + expect(criterion instanceof TelemetryCriterion).toBeTrue(); + expect(criterion.operator).toEqual(testConditionDefinition.operator); + expect(criterion.input).toEqual(testConditionDefinition.input); + expect(criterion.metaDataKey).toEqual(testConditionDefinition.metaDataKey); + expect(criterion.key).toEqual(testConditionDefinition.key); + }); + + it("initializes with the trigger from the condition definition", function () { + expect(conditionObj.trigger).toEqual(testConditionDefinition.trigger); + }); +}); From 5d8252bb079201a725b2f8e392b1b613b976e8b9 Mon Sep 17 00:00:00 2001 From: Joshi Date: Mon, 13 Jan 2020 13:55:54 -0800 Subject: [PATCH 06/10] Adds tests for condition class --- src/plugins/condition/Condition.js | 1 + src/plugins/condition/ConditionSpec.js | 9 ++++++++- src/plugins/condition/StyleRuleManager.js | 0 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 src/plugins/condition/StyleRuleManager.js diff --git a/src/plugins/condition/Condition.js b/src/plugins/condition/Condition.js index 5a5bb3e790..d16352bef9 100644 --- a/src/plugins/condition/Condition.js +++ b/src/plugins/condition/Condition.js @@ -175,6 +175,7 @@ export default class Condition extends EventEmitter { //TODO: implement as part of the evaluator class task. evaluate() { if (this.trigger === TRIGGER.ANY) { + criteria.evaluate(); this.result = false; } else if (this.trigger === TRIGGER.ALL) { this.result = false; diff --git a/src/plugins/condition/ConditionSpec.js b/src/plugins/condition/ConditionSpec.js index efb19d6fbe..c4d29545ac 100644 --- a/src/plugins/condition/ConditionSpec.js +++ b/src/plugins/condition/ConditionSpec.js @@ -57,8 +57,9 @@ describe("The condition", function () { openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString']); openmct.objects.get.and.returnValue(testTelemetryObject); openmct.objects.makeKeyString.and.returnValue(testTelemetryObject.identifier.key); - openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject']); + openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', 'subscribe']); openmct.telemetry.isTelemetryObject.and.returnValue(true); + openmct.telemetry.subscribe.and.returnValue(function () {}); testConditionDefinition = { trigger: TRIGGER.ANY, @@ -108,4 +109,10 @@ describe("The condition", function () { it("initializes with the trigger from the condition definition", function () { expect(conditionObj.trigger).toEqual(testConditionDefinition.trigger); }); + + it("destroys all criteria for a condition", function () { + const result = conditionObj.destroyCriteria(); + expect(result).toBeTrue(); + expect(conditionObj.criteria.length).toEqual(0); + }); }); diff --git a/src/plugins/condition/StyleRuleManager.js b/src/plugins/condition/StyleRuleManager.js new file mode 100644 index 0000000000..e69de29bb2 From 7cf6dc386f10b89f28c3d10b7b2856b19b62075d Mon Sep 17 00:00:00 2001 From: Joshi Date: Mon, 13 Jan 2020 13:58:25 -0800 Subject: [PATCH 07/10] Removes use of createOpenMCT and uses mock object for openmct instead. --- .../condition/criterion/TelemetryCriterion.js | 10 ++++++---- .../condition/criterion/TelemetryCriterionSpec.js | 14 ++++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/plugins/condition/criterion/TelemetryCriterion.js b/src/plugins/condition/criterion/TelemetryCriterion.js index 0637c34269..bfc38e2877 100644 --- a/src/plugins/condition/criterion/TelemetryCriterion.js +++ b/src/plugins/condition/criterion/TelemetryCriterion.js @@ -24,15 +24,15 @@ import * as EventEmitter from 'eventemitter3'; export default class TelemetryCriterion extends EventEmitter { - constructor(telemetryDomainObject, openmct) { + constructor(telemetryDomainObjectKey, openmct) { super(); - this.telemetryObject = telemetryDomainObject; this.openmct = openmct; + this.telemetryObject = this.openmct.objects.get(this.openmct.objects.makeKeyString(telemetryDomainObjectKey)); this.telemetryAPI = this.openmct.telemetry; this.subscription = null; - this.telemetryObjectIdAsString = null; this.telemetryMetadata = null; + this.telemetryObjectIdAsString = null; if (this.telemetryAPI.isTelemetryObject(this.telemetryObject)) { this.telemetryObjectIdAsString = this.openmct.objects.makeKeyString(this.telemetryObject.identifier); } @@ -73,7 +73,9 @@ export default class TelemetryCriterion extends EventEmitter { */ unsubscribe() { //unsubscribe from telemetry source - this.subscription(); + if (typeof this.subscription === 'function') { + this.subscription(); + } delete this.subscription; this.emit('criterion::Remove', this.telemetryObjectIdAsString); delete this.telemetryObjectIdAsString; diff --git a/src/plugins/condition/criterion/TelemetryCriterionSpec.js b/src/plugins/condition/criterion/TelemetryCriterionSpec.js index 302e706dd7..e85947f7ef 100644 --- a/src/plugins/condition/criterion/TelemetryCriterionSpec.js +++ b/src/plugins/condition/criterion/TelemetryCriterionSpec.js @@ -21,9 +21,8 @@ *****************************************************************************/ import TelemetryCriterion from "./TelemetryCriterion"; -import { createOpenMct } from "../../../testTools"; -let openmct = createOpenMct(), +let openmct = {}, mockListener, testTelemetryObject, telemetryCriterion; @@ -31,7 +30,6 @@ let openmct = createOpenMct(), describe("The telemetry criterion", function () { beforeEach (() => { - mockListener = jasmine.createSpy('listener'); testTelemetryObject = { identifier:{ namespace: "", key: "test-object"}, type: "test-object", @@ -52,9 +50,17 @@ describe("The telemetry criterion", function () { }] } }; + openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString', ]); + openmct.objects.get.and.returnValue(testTelemetryObject); + openmct.objects.makeKeyString.and.returnValue(testTelemetryObject.identifier.key); + openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', "subscribe"]); + openmct.telemetry.isTelemetryObject.and.returnValue(true); + openmct.telemetry.subscribe.and.returnValue(function () {}); + + mockListener = jasmine.createSpy('listener'); telemetryCriterion = new TelemetryCriterion( - testTelemetryObject, + testTelemetryObject.identifier, openmct ); From a6825f530c6a483e4ee974f823c68d32293e756d Mon Sep 17 00:00:00 2001 From: Joshi Date: Mon, 13 Jan 2020 14:05:43 -0800 Subject: [PATCH 08/10] Fixes failing test. --- src/plugins/condition/criterion/TelemetryCriterion.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/condition/criterion/TelemetryCriterion.js b/src/plugins/condition/criterion/TelemetryCriterion.js index 9f02851a6e..09846bf36a 100644 --- a/src/plugins/condition/criterion/TelemetryCriterion.js +++ b/src/plugins/condition/criterion/TelemetryCriterion.js @@ -73,7 +73,9 @@ export default class TelemetryCriterion extends EventEmitter { */ unsubscribe() { //unsubscribe from telemetry source - this.subscription(); + if (typeof this.subscription === 'function') { + this.subscription(); + } delete this.subscription; this.emit('criterion::Remove', this.telemetryObjectIdAsString); delete this.telemetryObjectIdAsString; From 1ffe76a5251acd7bfdbd0425b262c60aec5863dd Mon Sep 17 00:00:00 2001 From: Joshi Date: Mon, 13 Jan 2020 14:09:59 -0800 Subject: [PATCH 09/10] Fixes linting issue. --- src/plugins/condition/criterion/TelemetryCriterionSpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/condition/criterion/TelemetryCriterionSpec.js b/src/plugins/condition/criterion/TelemetryCriterionSpec.js index e85947f7ef..7218279e8b 100644 --- a/src/plugins/condition/criterion/TelemetryCriterionSpec.js +++ b/src/plugins/condition/criterion/TelemetryCriterionSpec.js @@ -50,7 +50,7 @@ describe("The telemetry criterion", function () { }] } }; - openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString', ]); + openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString']); openmct.objects.get.and.returnValue(testTelemetryObject); openmct.objects.makeKeyString.and.returnValue(testTelemetryObject.identifier.key); openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', "subscribe"]); From d15244043627e7b07456285ee7aa333601a3d6c4 Mon Sep 17 00:00:00 2001 From: Joshi Date: Mon, 13 Jan 2020 14:12:05 -0800 Subject: [PATCH 10/10] Fixes careless mistake - undefined object. --- src/plugins/condition/Condition.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/condition/Condition.js b/src/plugins/condition/Condition.js index d16352bef9..5a5bb3e790 100644 --- a/src/plugins/condition/Condition.js +++ b/src/plugins/condition/Condition.js @@ -175,7 +175,6 @@ export default class Condition extends EventEmitter { //TODO: implement as part of the evaluator class task. evaluate() { if (this.trigger === TRIGGER.ANY) { - criteria.evaluate(); this.result = false; } else if (this.trigger === TRIGGER.ALL) { this.result = false;