From 8e13819e1e2f93b0c19ba31a4a2fc9a7b4856330 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Wed, 15 Feb 2017 12:15:34 -0800 Subject: [PATCH 1/3] [API] composition providers receive new-style objects Ensure that composition providers get new-style objects (with id included) so that they can properly check for applicability. --- .../capabilities/TransactionCapabilityDecorator.js | 5 +++-- .../core/src/capabilities/CoreCapabilityProvider.js | 8 ++++---- .../src/QueuingPersistenceCapabilityDecorator.js | 4 ++-- .../test/QueuingPersistenceCapabilityDecoratorSpec.js | 6 ++++-- src/adapter/capabilities/APICapabilityDecorator.js | 7 ++++--- src/adapter/runs/AlternateCompositionInitializer.js | 11 ++++++++--- src/adapter/services/Instantiate.js | 2 +- 7 files changed, 26 insertions(+), 17 deletions(-) diff --git a/platform/commonUI/edit/src/capabilities/TransactionCapabilityDecorator.js b/platform/commonUI/edit/src/capabilities/TransactionCapabilityDecorator.js index 344f56435b..d93ccfb128 100644 --- a/platform/commonUI/edit/src/capabilities/TransactionCapabilityDecorator.js +++ b/platform/commonUI/edit/src/capabilities/TransactionCapabilityDecorator.js @@ -48,9 +48,10 @@ define( * Decorate PersistenceCapability to queue persistence calls when a * transaction is in progress. */ - TransactionCapabilityDecorator.prototype.getCapabilities = function (model) { + TransactionCapabilityDecorator.prototype.getCapabilities = function () { var self = this, - capabilities = this.capabilityService.getCapabilities(model), + capabilities = this.capabilityService.getCapabilities + .apply(this.capabilityService, arguments), persistenceCapability = capabilities.persistence; capabilities.persistence = function (domainObject) { diff --git a/platform/core/src/capabilities/CoreCapabilityProvider.js b/platform/core/src/capabilities/CoreCapabilityProvider.js index 412b7a57a2..c2dd75233d 100644 --- a/platform/core/src/capabilities/CoreCapabilityProvider.js +++ b/platform/core/src/capabilities/CoreCapabilityProvider.js @@ -53,10 +53,10 @@ define( */ function CoreCapabilityProvider(capabilities, $log) { // Filter by invoking the capability's appliesTo method - function filterCapabilities(model) { + function filterCapabilities(model, id) { return capabilities.filter(function (capability) { return capability.appliesTo ? - capability.appliesTo(model) : + capability.appliesTo(model, id) : true; }); } @@ -75,8 +75,8 @@ define( return result; } - function getCapabilities(model) { - return packageCapabilities(filterCapabilities(model)); + function getCapabilities(model, id) { + return packageCapabilities(filterCapabilities(model, id)); } return { diff --git a/platform/persistence/queue/src/QueuingPersistenceCapabilityDecorator.js b/platform/persistence/queue/src/QueuingPersistenceCapabilityDecorator.js index 904ed66f47..66213e59ce 100644 --- a/platform/persistence/queue/src/QueuingPersistenceCapabilityDecorator.js +++ b/platform/persistence/queue/src/QueuingPersistenceCapabilityDecorator.js @@ -50,7 +50,7 @@ define( this.capabilityService = capabilityService; } - QueuingPersistenceCapabilityDecorator.prototype.getCapabilities = function (model) { + QueuingPersistenceCapabilityDecorator.prototype.getCapabilities = function (model, id) { var capabilityService = this.capabilityService, persistenceQueue = this.persistenceQueue; @@ -76,7 +76,7 @@ define( } return decoratePersistence( - capabilityService.getCapabilities(model) + capabilityService.getCapabilities(model, id) ); }; diff --git a/platform/persistence/queue/test/QueuingPersistenceCapabilityDecoratorSpec.js b/platform/persistence/queue/test/QueuingPersistenceCapabilityDecoratorSpec.js index 57edcc70c8..fbdaa69304 100644 --- a/platform/persistence/queue/test/QueuingPersistenceCapabilityDecoratorSpec.js +++ b/platform/persistence/queue/test/QueuingPersistenceCapabilityDecoratorSpec.js @@ -32,6 +32,7 @@ define( mockPersistence, mockDomainObject, testModel, + testId, decorator; beforeEach(function () { @@ -41,6 +42,7 @@ define( ['getCapabilities'] ); testModel = { someKey: "some value" }; + testId = 'someId'; mockPersistence = jasmine.createSpyObj( 'persistence', ['persist', 'refresh'] @@ -67,9 +69,9 @@ define( // QueuingPersistenceCapability itself, which has its own tests. it("delegates to its wrapped service", function () { - decorator.getCapabilities(testModel); + decorator.getCapabilities(testModel, testId); expect(mockCapabilityService.getCapabilities) - .toHaveBeenCalledWith(testModel); + .toHaveBeenCalledWith(testModel, testId); }); it("wraps its persistence capability's constructor", function () { diff --git a/src/adapter/capabilities/APICapabilityDecorator.js b/src/adapter/capabilities/APICapabilityDecorator.js index 01bd6a32ef..8b964f553f 100644 --- a/src/adapter/capabilities/APICapabilityDecorator.js +++ b/src/adapter/capabilities/APICapabilityDecorator.js @@ -38,14 +38,15 @@ define([ } APICapabilityDecorator.prototype.getCapabilities = function ( - model + model, + id ) { - var capabilities = this.capabilityService.getCapabilities(model); + var capabilities = this.capabilityService.getCapabilities(model, id); if (capabilities.mutation) { capabilities.mutation = synchronizeMutationCapability(capabilities.mutation); } - if (AlternateCompositionCapability.appliesTo(model)) { + if (AlternateCompositionCapability.appliesTo(model, id)) { capabilities.composition = function (domainObject) { return new AlternateCompositionCapability(this.$injector, domainObject); }.bind(this); diff --git a/src/adapter/runs/AlternateCompositionInitializer.js b/src/adapter/runs/AlternateCompositionInitializer.js index 2129086b8c..ac815e48aa 100644 --- a/src/adapter/runs/AlternateCompositionInitializer.js +++ b/src/adapter/runs/AlternateCompositionInitializer.js @@ -21,13 +21,18 @@ *****************************************************************************/ define([ - '../capabilities/AlternateCompositionCapability' -], function (AlternateCompositionCapability) { + '../capabilities/AlternateCompositionCapability', + '../../api/objects/object-utils' +], function ( + AlternateCompositionCapability, + objectUtils +) { // Present to work around the need for openmct to be used // from AlternateCompositionCapability.appliesTo, even though it // cannot be injected. function AlternateCompositionInitializer(openmct) { - AlternateCompositionCapability.appliesTo = function (model) { + AlternateCompositionCapability.appliesTo = function (model, id) { + model = objectUtils.toNewFormat(model, id || ''); return !!openmct.composition.get(model); }; } diff --git a/src/adapter/services/Instantiate.js b/src/adapter/services/Instantiate.js index 3b4c190705..21c6f9742a 100644 --- a/src/adapter/services/Instantiate.js +++ b/src/adapter/services/Instantiate.js @@ -37,7 +37,7 @@ define( id = id || identifierService.generate(); var old_id = model.id; model.id = id; - var capabilities = capabilityService.getCapabilities(model); + var capabilities = capabilityService.getCapabilities(model, id); model.id = old_id; cacheService.put(id, model); return new DomainObjectImpl(id, model, capabilities); From d9f86224597b4324bb8d8fec13180d34f50e1ffd Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Tue, 21 Feb 2017 11:37:09 -0800 Subject: [PATCH 2/3] [Telemetry] Update TelemetryProvider API Based on feedback from tutorial sprint, update provider API, formatter API, and legacy adapter code. Providers can now implement separate checks for providing realtime and historical data, and providers are not registered with a specific strategy. Strategy is instead intended to be an attribute in the request options. Removed unused code in the telemetry API and simplify limitEvaluators. --- src/api/telemetry/LegacyTelemetryProvider.js | 58 ++++--- src/api/telemetry/TelemetryAPI.js | 166 ++++++------------- src/api/telemetry/TelemetryValueFormatter.js | 24 +-- 3 files changed, 99 insertions(+), 149 deletions(-) diff --git a/src/api/telemetry/LegacyTelemetryProvider.js b/src/api/telemetry/LegacyTelemetryProvider.js index f76fe99d10..3721e5af08 100644 --- a/src/api/telemetry/LegacyTelemetryProvider.js +++ b/src/api/telemetry/LegacyTelemetryProvider.js @@ -29,7 +29,8 @@ define([ * @implements module:openmct.TelemetryAPI~TelemetryProvider * @constructor */ - function LegacyTelemetryProvider(instantiate) { + function LegacyTelemetryProvider(openmct, instantiate) { + this.telemetryApi = openmct.telemetry; this.instantiate = instantiate; } @@ -44,23 +45,33 @@ define([ utils.makeKeyString(domainObject.identifier)).hasCapability("telemetry"); }; + LegacyTelemetryProvider.prototype.supportsRequest = + LegacyTelemetryProvider.prototype.supportsSubscribe = + LegacyTelemetryProvider.prototype.canProvideTelemetry; + function createDatum(domainObject, metadata, legacySeries, i) { + var datum; + if (legacySeries.getDatum) { - return legacySeries.getDatum(i); + datum = legacySeries.getDatum(i); + } else { + datum = {}; + metadata.valuesForHints(['x']).forEach(function (metadatum) { + datum[metadatum.key] = legacySeries.getDomainValue(i, metadatum.key); + }); + + metadata.valuesForHints(['y']).forEach(function (metadatum) { + datum[metadatum.key] = legacySeries.getRangeValue(i, metadatum.key); + }); } - var datum = {}; - metadata.domains.reduce(function (d, domain) { - d[domain.key] = legacySeries.getDomainValue(i, domain.key); - return d; - }, datum); - - metadata.ranges.reduce(function (d, range) { - d[range.key] = legacySeries.getRangeValue(i, range.key); - return d; - }, datum); - - datum.name = domainObject.name; + /** + * If telemetry metadata defines a 'name' field, and one is not present + * on the datum, add it. + */ + if (metadata.value('name') !== undefined && datum.name === undefined) { + datum.name = domainObject.name; + } return datum; } @@ -93,11 +104,12 @@ define([ * telemetry data. */ LegacyTelemetryProvider.prototype.request = function (domainObject, request) { + var metadata = this.telemetryApi.getMetadata(domainObject); var oldObject = this.instantiate(utils.toOldFormat(domainObject), utils.makeKeyString(domainObject.identifier)); var capability = oldObject.getCapability("telemetry"); return capability.requestData(request).then(function (telemetrySeries) { - return Promise.resolve(adaptSeries(domainObject, capability.getMetadata(), telemetrySeries)); + return Promise.resolve(adaptSeries(domainObject, metadata, telemetrySeries)); }).catch(function (error) { return Promise.reject(error); }); @@ -118,11 +130,12 @@ define([ * @returns {platform|telemetry.TelemetrySubscription|*} */ LegacyTelemetryProvider.prototype.subscribe = function (domainObject, callback, request) { + var metadata = this.telemetryApi.getMetadata(domainObject); var oldObject = this.instantiate(utils.toOldFormat(domainObject), utils.makeKeyString(domainObject.identifier)); var capability = oldObject.getCapability("telemetry"); function callbackWrapper(series) { - callback(createDatum(domainObject, capability.getMetadata(), series, series.getPointCount() - 1)); + callback(createDatum(domainObject, metadata, series, series.getPointCount() - 1)); } return capability.subscribe(callbackWrapper, request); @@ -134,6 +147,10 @@ define([ utils.makeKeyString(domainObject.identifier)); var limitEvaluator = oldObject.getCapability("limit"); + if (!limitEvaluator) { + return; + } + return { evaluate: function (datum, property) { return limitEvaluator.evaluate(datum, property.key); @@ -142,10 +159,11 @@ define([ }; return function (openmct, instantiate) { - // Push onto the start of the default providers array so that it's - // always the last resort - openmct.telemetry.defaultProviders.unshift( - new LegacyTelemetryProvider(instantiate)); + // Legacy provider should always be the fallback. + var provider = new LegacyTelemetryProvider(openmct, instantiate); + openmct.telemetry.legacyProvider = provider; + openmct.telemetry.requestProviders.push(provider); + openmct.telemetry.subscriptionProviders.push(provider); }; }); diff --git a/src/api/telemetry/TelemetryAPI.js b/src/api/telemetry/TelemetryAPI.js index 183432eb95..0b6bbb479e 100644 --- a/src/api/telemetry/TelemetryAPI.js +++ b/src/api/telemetry/TelemetryAPI.js @@ -74,34 +74,6 @@ define([ * @memberof module:openmct.TelemetryAPI~TelemetryFormatter# */ - - - - // format map is a placeholder until we figure out format service. - var FORMAT_MAP = { - generic: function (range) { - return function (datum) { - return datum[range.key]; - }; - }, - enum: function (range) { - var enumMap = _.indexBy(range.enumerations, 'value'); - return function (datum) { - try { - return enumMap[datum[range.valueKey]].text; - } catch (e) { - return datum[range.valueKey]; - } - }; - } - }; - - FORMAT_MAP.number = - FORMAT_MAP.float = - FORMAT_MAP.integer = - FORMAT_MAP.ascii = - FORMAT_MAP.generic; - /** * Describes a property which would be found in a datum of telemetry * associated with a particular domain object. @@ -161,8 +133,8 @@ define([ */ function TelemetryAPI(MCT) { this.MCT = MCT; - this.providersByStrategy = {}; - this.defaultProviders = []; + this.requestProviders = []; + this.subscriptionProviders = []; this.metadataCache = new WeakMap(); this.formatMapCache = new WeakMap(); this.valueFormatterCache = new WeakMap(); @@ -179,9 +151,8 @@ define([ * @memberof module:openmct.TelemetryAPI~TelemetryProvider# */ TelemetryAPI.prototype.canProvideTelemetry = function (domainObject) { - return this.defaultProviders.some(function (provider) { - return provider.canProvideTelemetry(domainObject); - }); + return !!this.findSubscriptionProvider(domainObject) || + !!this.findRequestProvider(domainObject); }; /** @@ -191,39 +162,36 @@ define([ * @memberof module:openmct.TelemetryAPI# * @param {module:openmct.TelemetryAPI~TelemetryProvider} provider the new * telemetry provider - * @param {string} [strategy] the request strategy supported by - * this provider. If omitted, this will be used as a - * default provider (when no strategy is requested or no - * matching strategy is found.) */ - TelemetryAPI.prototype.addProvider = function (provider, strategy) { - if (!strategy) { - this.defaultProviders.push(provider); - } else { - this.providersByStrategy[strategy] = - this.providersByStrategy[strategy] || []; - this.providersByStrategy[strategy].push(provider); + TelemetryAPI.prototype.addProvider = function (provider) { + if (provider.supportsRequest) { + this.requestProviders.unshift(provider); + } + if (provider.supportsSubscribe) { + this.subscriptionProviders.unshift(provider); } }; /** * @private */ - TelemetryAPI.prototype.findProvider = function (domainObject, strategy) { + TelemetryAPI.prototype.findSubscriptionProvider = function (domainObject) { function supportsDomainObject(provider) { - return provider.canProvideTelemetry(domainObject); + return provider.supportsSubscribe(domainObject); } - if (strategy) { - var eligibleProviders = - (this.providersByStrategy[strategy] || []) - .filter(supportsDomainObject); - if (eligibleProviders.length > 0) { - return eligibleProviders[0]; - } + return this.subscriptionProviders.filter(supportsDomainObject)[0]; + }; + + /** + * @private + */ + TelemetryAPI.prototype.findRequestProvider = function (domainObject, options) { + function supportsDomainObject(provider) { + return provider.supportsRequest(domainObject); } - return this.defaultProviders.filter(supportsDomainObject)[0]; + return this.requestProviders.filter(supportsDomainObject)[0]; }; /** @@ -242,10 +210,29 @@ define([ * telemetry data */ TelemetryAPI.prototype.request = function (domainObject, options) { - var provider = this.findProvider(domainObject, options.strategy); - return provider ? - provider.request(domainObject, options) : - Promise.reject([]); + var provider = this.findRequestProvider.apply(this, arguments); + return provider.request.apply(provider, arguments); + }; + + /** + * Subscribe to realtime telemetry for a specific domain object. + * The callback will be called whenever data is received from a + * realtime provider. + * + * @method subscribe + * @memberof module:openmct.TelemetryAPI~TelemetryProvider# + * @param {module:openmct.DomainObject} domainObject the object + * which has associated telemetry + * @param {Function} callback the callback to invoke with new data, as + * it becomes available + * @param {module:openmct.TelemetryAPI~TelemetryRequest} options + * options for this request + * @returns {Function} a function which may be called to terminate + * the subscription + */ + TelemetryAPI.prototype.subscribe = function (domainObject, callback, options) { + var provider = this.findSubscriptionProvider.apply(this, arguments); + return provider.subscribe.apply(provider, arguments); }; /** @@ -327,53 +314,6 @@ define([ return this.formatMapCache.get(metadata); }; - /** - * Subscribe to realtime telemetry for a specific domain object. - * The callback will be called whenever data is received from a - * realtime provider. - * - * @method subscribe - * @memberof module:openmct.TelemetryAPI~TelemetryProvider# - * @param {module:openmct.DomainObject} domainObject the object - * which has associated telemetry - * @param {Function} callback the callback to invoke with new data, as - * it becomes available - * @param {module:openmct.TelemetryAPI~TelemetryRequest} options - * options for this request - * @returns {Function} a function which may be called to terminate - * the subscription - */ - - /** - * Get a list of all telemetry properties defined for this - * domain object. - * - * @param {module:openmct.DomainObject} domainObject the domain - * object for which to request telemetry - * @returns {module:openmct.TelemetryAPI~TelemetryProperty[]} - * telemetry metadata - * @method properties - * @memberof module:openmct.TelemetryAPI~TelemetryProvider# - */ - - /** - * Telemetry formatters help you format telemetry values for - * display. Under the covers, they use telemetry metadata to - * interpret your telemetry data, and then they use the format API - * to format that data for display. - * - * This method is optional. - * If a provider does not implement this method, it is presumed - * that all telemetry associated with this domain object can - * be formatted correctly by string coercion. - * - * @param {module:openmct.DomainObject} domainObject the domain - * object for which to format telemetry - * @returns {module:openmct.TelemetryAPI~TelemetryFormatter} - * @method formatter - * @memberof module:openmct.TelemetryAPI~TelemetryProvider# - */ - /** * Get a limit evaluator for this domain object. * Limit Evaluators help you evaluate limit and alarm status of individual telemetry datums for display purposes without having to interact directly with the Limit API. @@ -388,19 +328,9 @@ define([ * @method limitEvaluator * @memberof module:openmct.TelemetryAPI~TelemetryProvider# */ - _.forEach({ - subscribe: undefined, - properties: [], - formatter: undefined, - limitEvaluator: undefined - }, function (defaultValue, method) { - TelemetryAPI.prototype[method] = function (domainObject) { - var provider = this.findProvider(domainObject); - return provider ? - provider[method].apply(provider, arguments) : - defaultValue; - }; - }); + TelemetryAPI.prototype.limitEvaluator = function () { + return this.legacyProvider.limitEvaluator.apply(this.legacyProvider, arguments); + }; return TelemetryAPI; }); diff --git a/src/api/telemetry/TelemetryValueFormatter.js b/src/api/telemetry/TelemetryValueFormatter.js index 801aee1176..a5e8cb8720 100644 --- a/src/api/telemetry/TelemetryValueFormatter.js +++ b/src/api/telemetry/TelemetryValueFormatter.js @@ -28,6 +28,18 @@ define([ // TODO: needs reference to formatService; function TelemetryValueFormatter(valueMetadata, formatService) { + var numberFormatter = { + parse: function (x) { + return Number(x); + }, + format: function (x) { + return x; + }, + validate: function (x) { + return true; + } + }; + this.valueMetadata = valueMetadata; this.parseCache = new WeakMap(); this.formatCache = new WeakMap(); @@ -36,17 +48,7 @@ define([ .getFormat(valueMetadata.format, valueMetadata); } catch (e) { // TODO: Better formatting - this.formatter = { - parse: function (x) { - return Number(x); - }, - format: function (x) { - return x; - }, - validate: function (x) { - return true; - } - }; + this.formatter = numberFormatter; } if (valueMetadata.type === 'enum') { From 8c77d4006acf1f093dcbd049c0cf50444de46974 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Tue, 21 Feb 2017 17:03:16 -0800 Subject: [PATCH 3/3] Pass options to support checks --- src/api/telemetry/TelemetryAPI.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/api/telemetry/TelemetryAPI.js b/src/api/telemetry/TelemetryAPI.js index 0b6bbb479e..84fb7e1e70 100644 --- a/src/api/telemetry/TelemetryAPI.js +++ b/src/api/telemetry/TelemetryAPI.js @@ -176,8 +176,9 @@ define([ * @private */ TelemetryAPI.prototype.findSubscriptionProvider = function (domainObject) { + var args = Array.prototype.slice.apply(arguments); function supportsDomainObject(provider) { - return provider.supportsSubscribe(domainObject); + return provider.supportsSubscribe.apply(provider, args); } return this.subscriptionProviders.filter(supportsDomainObject)[0]; @@ -187,8 +188,9 @@ define([ * @private */ TelemetryAPI.prototype.findRequestProvider = function (domainObject, options) { + var args = Array.prototype.slice.apply(arguments); function supportsDomainObject(provider) { - return provider.supportsRequest(domainObject); + return provider.supportsRequest.apply(provider, args); } return this.requestProviders.filter(supportsDomainObject)[0];