[Telemetry] Add JSDoc

Add JSDoc and in-line comments to scripts in the
platform/telemetry bundle, for WTD-575.
This commit is contained in:
Victor Woeltjen 2014-11-28 20:59:41 -08:00
parent f8a0544435
commit 55c2d15cdc
4 changed files with 190 additions and 10 deletions

View File

@ -9,17 +9,20 @@ define(
"use strict";
/**
* A telemetry aggregator makes many telemetry providers
* appear as one.
*
* @constructor
*/
function TelemetryAggregator($q, telemetryProviders) {
// Merge the results from many providers into one
// result object.
function mergeResults(results) {
var merged = {};
results.forEach(function (result) {
Object.keys(result).forEach(function (k) {
// Otherwise, just take the result
merged[k] = result[k];
});
});
@ -27,6 +30,8 @@ define(
return merged;
}
// Request telemetry from all providers; once they've
// responded, merge the results into one result object.
function requestTelemetry(requests) {
return $q.all(telemetryProviders.map(function (provider) {
return provider.requestTelemetry(requests);
@ -34,6 +39,14 @@ define(
}
return {
/**
* Request telemetry data.
* @param {TelemetryRequest[]} requests and array of
* requests to be handled
* @returns {Promise} a promise for telemetry data
* which may (or may not, depending on
* availability) satisfy the requests
*/
requestTelemetry: requestTelemetry
};
}

View File

@ -9,6 +9,9 @@ define(
"use strict";
/**
* A telemetry capability provides a means of requesting telemetry
* for a specific object, and for unwrapping the response (to get
* at the specific data which is appropriate to the domain object.)
*
* @constructor
*/
@ -23,6 +26,8 @@ define(
telemetryService =
$q.when($injector.get("telemetryService"));
} catch (e) {
// $injector should throw is telemetryService
// is unavailable or unsatisfiable.
$log.warn("Telemetry service unavailable");
telemetryService = $q.reject(e);
}
@ -30,21 +35,30 @@ define(
return telemetryService;
}
// Build a request object. This takes the request that was
// passed to the capability, and adds source, id, and key
// fields associated with the object (from its type definition
// and/or its model)
function buildRequest(request) {
// Start with any "telemetry" field in type; use that as a
// basis for the request.
var type = domainObject.getCapability("type"),
typeRequest = (type && type.getDefinition().telemetry) || {},
modelTelemetry = domainObject.getModel().telemetry,
fullRequest = Object.create(typeRequest);
// Add properties from the telemetry field of this
// specific domain object.
Object.keys(modelTelemetry).forEach(function (k) {
fullRequest[k] = modelTelemetry[k];
});
// Add properties from this specific requestData call.
Object.keys(request).forEach(function (k) {
fullRequest[k] = request[k];
});
// Include domain object ID, at minimum
// Ensure an ID and key are present
if (!fullRequest.id) {
fullRequest.id = domainObject.getId();
}
@ -55,37 +69,63 @@ define(
return fullRequest;
}
// Issue a request for telemetry data
function requestTelemetry(request) {
// Bring in any defaults from the object model
var fullRequest = buildRequest(request || {}),
source = fullRequest.source,
key = fullRequest.key;
// Pull out the relevant field from the larger,
// structured response.
function getRelevantResponse(response) {
return ((response || {})[source] || {})[key] || {};
}
// Issue a request to the service
function requestTelemetryFromService(telemetryService) {
return telemetryService.requestTelemetry([fullRequest]);
}
// If a telemetryService is not available,
// getTelemetryService() should reject, and this should
// bubble through subsequent then calls.
return getTelemetryService()
.then(requestTelemetryFromService)
.then(getRelevantResponse);
}
return {
/**
* Request telemetry data for this specific domain object.
* @param {TelemetryRequest} [request] parameters for this
* specific request
* @returns {Promise} a promise for the resulting telemetry
* object
*/
requestData: requestTelemetry,
/**
* Get metadata about this domain object's associated
* telemetry.
*/
getMetadata: function () {
// metadata just looks like a request,
// so use buildRequest to bring in both
// type-level and object-level telemetry
// properties
return buildRequest({});
}
};
}
/**
* The telemetry capability is applicable when a
* domain object model has a "telemetry" field.
*/
TelemetryCapability.appliesTo = function (model) {
return (model &&
model.telemetry &&
model.telemetry.source) ? true : false;
model.telemetry) ? true : false;
};
return TelemetryCapability;

View File

@ -17,19 +17,44 @@ define(
*/
function TelemetryController($scope, $q, $timeout, $log) {
// Private to maintain in this scope
var self = {
// IDs of domain objects with telemetry
ids: [],
// Containers for latest responses (id->response)
// Used internally; see buildResponseContainer
// for format
response: {},
// Request fields (id->requests)
request: {},
// Number of outstanding requests
pending: 0,
// Array of object metadatas, for easy retrieval
metadatas: [],
// Interval at which to poll for new data
interval: 1000,
// Flag tracking whether or not a request
// is in progress
refreshing: false,
// Used to track whether a new telemetryUpdate
// is being issued.
broadcasting: false
};
// Broadcast that a telemetryUpdate has occurred.
function doBroadcast() {
// This may get called multiple times from
// multiple objects, so set a flag to suppress
// multiple simultaneous events from being
// broadcast, then issue the actual broadcast
// later (via $timeout)
if (!self.broadcasting) {
self.broadcasting = true;
$timeout(function () {
@ -39,11 +64,14 @@ define(
}
}
// Issue a request for new telemetry for one of the
// objects being tracked by this controller
function requestTelemetryForId(id, trackPending) {
var responseObject = self.response[id],
domainObject = responseObject.domainObject,
telemetry = domainObject.getCapability('telemetry');
// Callback for when data comes back
function storeData(data) {
self.pending -= trackPending ? 1 : 0;
responseObject.data = data;
@ -52,30 +80,50 @@ define(
self.pending += trackPending ? 1 : 0;
// Shouldn't happen, but isn't fatal,
// so warn.
if (!telemetry) {
$log.warn([
"Expected telemetry capability for ",
id,
" but found none. Cannot request data."
].join(""));
// Request won't happen, so don't
// mark it as pending.
self.pending -= trackPending ? 1 : 0;
return;
}
// Issue the request using the object's telemetry capability
return $q.when(telemetry.requestData(self.request))
.then(storeData);
}
// Request telemetry for all objects tracked by this
// controller. A flag is passed to indicate whether the
// pending counter should be incremented (this will
// cause isRequestPending() to change, which we only
// want to happen for requests which have originated
// outside of this controller's polling action.)
function requestTelemetry(trackPending) {
return $q.all(self.ids.map(function (id) {
return requestTelemetryForId(id, trackPending);
}));
}
// Look up domain objects which have telemetry capabilities.
// This will either be the object in view, or object that
// this object delegates its telemetry capability to.
function promiseRelevantDomainObjects(domainObject) {
// If object has been cleared, there are no relevant
// telemetry-providing domain objects.
if (!domainObject) {
return $q.when([]);
}
// Otherwise, try delegation first, and attach the
// object itself if it has a telemetry capability.
return $q.when(domainObject.useCapability(
"delegation",
"telemetry"
@ -87,6 +135,9 @@ define(
});
}
// Build the response containers that are used internally
// by this controller to track latest responses, etc, for
// a given domain object.
function buildResponseContainer(domainObject) {
var telemetry = domainObject &&
domainObject.getCapability("telemetry"),
@ -103,12 +154,17 @@ define(
data: {}
};
} else {
// Shouldn't happen, as we've checked for
// telemetry capabilities previously, but
// be defensive.
$log.warn([
"Expected telemetry capability for ",
domainObject.getId(),
" but none was found."
].join(""));
// Create an empty container so subsequent
// behavior won't hit an exception.
self.response[domainObject.getId()] = {
name: domainObject.getModel().name,
domainObject: domainObject,
@ -119,11 +175,21 @@ define(
}
}
// Build response containers (as above) for all
// domain objects, and update some controller-internal
// state to support subsequent calls.
function buildResponseContainers(domainObjects) {
// Build the containers
domainObjects.forEach(buildResponseContainer);
// Maintain a list of relevant ids, to convert
// back from dictionary-like container objects to arrays.
self.ids = domainObjects.map(function (obj) {
return obj.getId();
});
// Keep a reference to all applicable metadata
// to return from getMetadata
self.metadatas = self.ids.map(function (id) {
return self.response[id].metadata;
});
@ -135,11 +201,16 @@ define(
}
}
// Get relevant telemetry-providing domain objects
// for the domain object which is represented in this
// scope. This will be the domain object itself, or
// its telemetry delegates, or both.
function getTelemetryObjects(domainObject) {
promiseRelevantDomainObjects(domainObject)
.then(buildResponseContainers);
}
// Handle a polling refresh interval
function startTimeout() {
if (!self.refreshing && self.interval !== undefined) {
self.refreshing = true;
@ -154,19 +225,58 @@ define(
}
}
// Watch for a represented domain object
$scope.$watch("domainObject", getTelemetryObjects);
startTimeout(); // Begin refreshing
// Begin polling for data changes
startTimeout();
return {
/**
* Get all telemetry metadata associated with
* telemetry-providing domain objects managed by
* this controller.
*
* This will ordered in the
* same manner as `getTelemetryObjects()` or
* `getResponse()`; that is, the metadata at a
* given index will correspond to the telemetry-providing
* domain object at the same index.
* @returns {Array} an array of metadata objects
*/
getMetadata: function () {
return self.metadatas;
},
/**
* Get all telemetry-providing domain objects managed by
* this controller.
*
* This will ordered in the
* same manner as `getMetadata()` or
* `getResponse()`; that is, the metadata at a
* given index will correspond to the telemetry-providing
* domain object at the same index.
* @returns {DomainObject[]} an array of metadata objects
*/
getTelemetryObjects: function () {
return self.ids.map(function (id) {
return self.response[id].domainObject;
});
},
/**
* Get the latest telemetry response for a specific
* domain object (if an argument is given) or for all
* objects managed by this controller (if no argument
* is supplied.)
*
* In the first form, this returns a single object; in
* the second form, it returns an array ordered in
* same manner as `getMetadata()` or
* `getTelemetryObjects()`; that is, the telemetry
* response at agiven index will correspond to the
* telemetry-providing domain object at the same index.
* @returns {Array} an array of responses
*/
getResponse: function getResponse(arg) {
var id = arg && (typeof arg === 'string' ?
arg : arg.getId());
@ -177,13 +287,33 @@ define(
return (self.ids || []).map(getResponse);
},
/**
* Check if the latest request (not counting
* requests from TelemtryController's own polling)
* is still outstanding. Users of the TelemetryController
* may use this method as a condition upon which to
* show user feedback, such as a wait spinner.
*
* @returns {boolean} true if the request is still outstanding
*/
isRequestPending: function () {
return self.pending > 0;
},
/**
* Issue a new data request. This will change the
* request parameters that are passed along to all
* telemetry capabilities managed by this controller.
*/
requestData: function (request) {
self.request = request || {};
return requestTelemetry(true);
},
/**
* Change the interval at which this controller will
* perform its polling activity.
* @param {number} durationMillis the interval at
* which to poll, in milliseconds
*/
setRefreshInterval: function (durationMillis) {
self.interval = durationMillis;
startTimeout();

View File

@ -65,9 +65,6 @@ define(
expect(TelemetryCapability.appliesTo({
telemetry: { source: "testSource" }
})).toBeTruthy();
expect(TelemetryCapability.appliesTo({
telemetry: { xsource: "testSource" }
})).toBeFalsy();
expect(TelemetryCapability.appliesTo({
xtelemetry: { source: "testSource" }
})).toBeFalsy();