Commit 9f04c502 by Torkel Ödegaard

refactoring graphiteSrv to use angular promises, removed dependency on RQ

parent 2e53450a
......@@ -43,20 +43,12 @@ require.config({
modernizr: '../vendor/modernizr-2.6.1',
elasticjs: '../vendor/elasticjs/elastic-angular-client',
rq: '../vendor/rq',
setImmediate: '../vendor/setImmediate',
},
shim: {
underscore: {
exports: '_'
},
rq: {
deps: ['setImmediate'],
exports: 'RQ'
},
angular: {
deps: ['jquery','config'],
exports: 'angular'
......
define([
'jquery',
'rq',
'underscore',
'config'
],
function ($, RQ, _, config) {
'use strict';
function build_graphite_options(options) {
var clean_options = [];
var graphite_options = ['target', 'targets', 'from', 'until', 'rawData', 'format', 'maxDataPoints'];
options['format'] = 'json';
$.each(options, function (key, value) {
if ($.inArray(key, graphite_options) === -1) {
return;
}
if (key === "targets") {
$.each(value, function (index, value) {
if (!value.hide) {
clean_options.push("target=" + encodeURIComponent(value.target));
}
});
} else if (value !== null) {
clean_options.push(key + "=" + encodeURIComponent(value));
}
});
return clean_options;
}
function loadGraphiteData(options)
{
return function (requestion) {
var graphOptions = {
from: $.plot.formatDate(options.range.from, '%H%:%M_%Y%m%d'),
until: $.plot.formatDate(options.range.to, '%H%:%M_%Y%m%d'),
targets: options.targets,
maxDataPoints: options.maxDataPoints
};
var graphiteParameters = build_graphite_options(graphOptions);
getGraphiteData(graphiteParameters)
.done(function(data) {
requestion(data);
})
.fail(function() {
requestion(null, 'Error in ajax call to graphite');
});
};
}
function getGraphiteData(parameters) {
return $.ajax({
accepts: { text: 'application/json' },
cache: false,
dataType: 'json',
url: config.graphiteUrl + '/render/',
type: "POST",
data: parameters.join('&')
});
}
function match(targets, graphiteTargetStr) {
var found = targets[0];
for (var i = 0; i < targets.length; i++) {
if (targets[i].target === graphiteTargetStr) {
found = targets[i];
break;
}
if(targets[i].target.match("'" + graphiteTargetStr + "'")) {
found = targets[i];
}
}
return found;
}
return {
loadGraphiteData: loadGraphiteData,
match: match
};
});
\ No newline at end of file
......@@ -19,8 +19,6 @@ define([
'kbn',
'moment',
'./timeSeries',
'./graphiteSrv',
'rq',
'jquery.flot',
'jquery.flot.events',
'jquery.flot.selection',
......@@ -29,14 +27,15 @@ define([
'jquery.flot.stack',
'jquery.flot.stackpercent'
],
function (angular, app, $, _, kbn, moment, timeSeries, graphiteSrv, RQ) {
function (angular, app, $, _, kbn, moment, timeSeries) {
'use strict';
var module = angular.module('kibana.panels.graphite', []);
app.useModule(module);
module.controller('graphite', function($scope, $rootScope, filterSrv) {
module.controller('graphite', function($scope, $rootScope, filterSrv, graphiteSrv, $timeout) {
$scope.panelMeta = {
modals : [
{
......@@ -211,7 +210,7 @@ function (angular, app, $, _, kbn, moment, timeSeries, graphiteSrv, RQ) {
$scope.init = function() {
$scope.openConfigureModal();
//$scope.openConfigureModal();
// Hide view options by default
$scope.options = false;
......@@ -313,84 +312,71 @@ function (angular, app, $, _, kbn, moment, timeSeries, graphiteSrv, RQ) {
var range = $scope.get_time_range();
var interval = $scope.get_interval(range);
var graphiteLoadOptions = {
var graphiteQuery = {
range: range,
targets: $scope.panel.targets,
maxDataPoints: $scope.panel.span * 50
};
var result = RQ.sequence([
graphiteSrv.loadGraphiteData(graphiteLoadOptions),
$scope.receiveGraphiteData(range, interval)
]);
result(function (data, failure) {
$scope.panelMeta.loading = false;
if (failure || !data) {
$scope.panel.error = 'Failed to do fetch graphite data: ' + failure;
$scope.$apply();
return;
}
$scope.$apply();
// Tell the histogram directive to render.
$scope.$emit('render', data);
});
return graphiteSrv.query(graphiteQuery)
.then(function(results) {
$scope.panelMeta.loading = false;
var data = $scope.receiveGraphiteData(results, range, interval)
$scope.$emit('render', data);
})
.then(null, function(err) {
$scope.panel.error = err.message || "Graphite HTTP Request Error";
});
};
$scope.receiveGraphiteData = function(range, interval) {
return function receive_graphite_data_requestor(requestion, results) {
$scope.legend = [];
var data = [];
$scope.receiveGraphiteData = function(results, range, interval) {
var results = results.data;
$scope.legend = [];
var data = [];
if(results.length === 0 ) {
requestion('no data in response from graphite');
}
if(results.length === 0 ) {
throw { message: 'no data in response from graphite' };
}
//console.log('Data from graphite:', results);
//console.log('nr datapoints from graphite: %d', results[0].datapoints.length);
//console.log('Data from graphite:', results);
//console.log('nr datapoints from graphite: %d', results[0].datapoints.length);
var tsOpts = {
interval: interval,
start_date: range && range.from,
end_date: range && range.to,
fill_style: 'no'
};
_.each(results, function(targetData) {
var time_series = new timeSeries.ZeroFilled(tsOpts);
var tsOpts = {
interval: interval,
start_date: range && range.from,
end_date: range && range.to,
fill_style: 'no'
};
_.each(targetData.datapoints, function(valueArray) {
if (valueArray[0]) {
time_series.addValue(valueArray[1] * 1000, valueArray[0]);
}
});
_.each(results, function(targetData) {
var time_series = new timeSeries.ZeroFilled(tsOpts);
var target = graphiteSrv.match($scope.panel.targets, targetData.target);
_.each(targetData.datapoints, function(valueArray) {
if (valueArray[0]) {
time_series.addValue(valueArray[1] * 1000, valueArray[0]);
}
});
var seriesInfo = {
alias: targetData.target,
color: $scope.colors[data.length],
enable: true,
yaxis: target.yaxis || 1
};
var target = graphiteSrv.match($scope.panel.targets, targetData.target);
$scope.legend.push(seriesInfo);
var seriesInfo = {
alias: targetData.target,
color: $scope.colors[data.length],
enable: true,
yaxis: target.yaxis || 1
};
data.push({
info: seriesInfo,
time_series: time_series,
yaxis: target.yaxis || 1
});
$scope.legend.push(seriesInfo);
data.push({
info: seriesInfo,
time_series: time_series,
yaxis: target.yaxis || 1
});
requestion(data);
};
});
return data;
};
$scope.add_target = function() {
......@@ -413,7 +399,7 @@ function (angular, app, $, _, kbn, moment, timeSeries, graphiteSrv, RQ) {
closeEditMode();
}
setImmediate(function() {
$timeout(function() {
$scope.$emit('render');
});
});
......
......@@ -4,6 +4,7 @@ define([
'./filterSrv',
'./timer',
'./panelMove',
'./graphite/graphiteSrv',
'./esVersion',
'./keyboardManager',
],
......
define([
'angular',
'underscore',
'jquery',
'config'
],
function () {
function (angular, _, $, config) {
'use strict';
var module = angular.module('kibana.services');
module.service('graphiteSrv', function($http) {
this.query = function(options) {
var graphOptions = {
from: $.plot.formatDate(options.range.from, '%H%:%M_%Y%m%d'),
until: $.plot.formatDate(options.range.to, '%H%:%M_%Y%m%d'),
targets: options.targets,
maxDataPoints: options.maxDataPoints
};
var params = buildGraphitePostParams(graphOptions);
var url = config.graphiteUrl + '/render/';
return $http({
method: 'POST',
url: url,
data: params.join('&'),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
});
};
this.match = function(targets, graphiteTargetStr) {
var found = targets[0];
for (var i = 0; i < targets.length; i++) {
if (targets[i].target === graphiteTargetStr) {
found = targets[i];
break;
}
if(targets[i].target.match("'" + graphiteTargetStr + "'")) {
found = targets[i];
}
}
return found;
};
/*function getGraphiteData(parameters) {
return $.ajax({
accepts: { text: 'application/json' },
cache: false,
dataType: 'json',
url: config.graphiteUrl + '/render/',
type: "POST",
data: parameters.join('&')
});
}
*/
function buildGraphitePostParams(options) {
var clean_options = [];
var graphite_options = ['target', 'targets', 'from', 'until', 'rawData', 'format', 'maxDataPoints'];
options['format'] = 'json';
$.each(options, function (key, value) {
if ($.inArray(key, graphite_options) === -1) {
return;
}
if (key === "targets") {
$.each(value, function (index, value) {
if (!value.hide) {
clean_options.push("target=" + encodeURIComponent(value.target));
}
});
} else if (value !== null) {
clean_options.push(key + "=" + encodeURIComponent(value));
}
});
return clean_options;
}
});
});
\ No newline at end of file
(function (global, undefined) {
"use strict";
var tasks = (function () {
function Task(handler, args) {
this.handler = handler;
this.args = args;
}
Task.prototype.run = function () {
// See steps in section 5 of the spec.
if (typeof this.handler === "function") {
// Choice of `thisArg` is not in the setImmediate spec; `undefined` is in the setTimeout spec though:
// http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html
this.handler.apply(undefined, this.args);
} else {
var scriptSource = "" + this.handler;
/*jshint evil: true */
eval(scriptSource);
}
};
var nextHandle = 1; // Spec says greater than zero
var tasksByHandle = {};
var currentlyRunningATask = false;
return {
addFromSetImmediateArguments: function (args) {
var handler = args[0];
var argsToHandle = Array.prototype.slice.call(args, 1);
var task = new Task(handler, argsToHandle);
var thisHandle = nextHandle++;
tasksByHandle[thisHandle] = task;
return thisHandle;
},
runIfPresent: function (handle) {
// From the spec: "Wait until any invocations of this algorithm started before this one have completed."
// So if we're currently running a task, we'll need to delay this invocation.
if (!currentlyRunningATask) {
var task = tasksByHandle[handle];
if (task) {
currentlyRunningATask = true;
try {
task.run();
} finally {
delete tasksByHandle[handle];
currentlyRunningATask = false;
}
}
} else {
// Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a
// "too much recursion" error.
global.setTimeout(function () {
tasks.runIfPresent(handle);
}, 0);
}
},
remove: function (handle) {
delete tasksByHandle[handle];
}
};
}());
function canUseNextTick() {
// Don't get fooled by e.g. browserify environments.
return typeof process === "object" &&
Object.prototype.toString.call(process) === "[object process]";
}
function canUseMessageChannel() {
return !!global.MessageChannel;
}
function canUsePostMessage() {
// The test against `importScripts` prevents this implementation from being installed inside a web worker,
// where `global.postMessage` means something completely different and can't be used for this purpose.
if (!global.postMessage || global.importScripts) {
return false;
}
var postMessageIsAsynchronous = true;
var oldOnMessage = global.onmessage;
global.onmessage = function () {
postMessageIsAsynchronous = false;
};
global.postMessage("", "*");
global.onmessage = oldOnMessage;
return postMessageIsAsynchronous;
}
function canUseReadyStateChange() {
return "document" in global && "onreadystatechange" in global.document.createElement("script");
}
function installNextTickImplementation(attachTo) {
attachTo.setImmediate = function () {
var handle = tasks.addFromSetImmediateArguments(arguments);
process.nextTick(function () {
tasks.runIfPresent(handle);
});
return handle;
};
}
function installMessageChannelImplementation(attachTo) {
var channel = new global.MessageChannel();
channel.port1.onmessage = function (event) {
var handle = event.data;
tasks.runIfPresent(handle);
};
attachTo.setImmediate = function () {
var handle = tasks.addFromSetImmediateArguments(arguments);
channel.port2.postMessage(handle);
return handle;
};
}
function installPostMessageImplementation(attachTo) {
// Installs an event handler on `global` for the `message` event: see
// * https://developer.mozilla.org/en/DOM/window.postMessage
// * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages
var MESSAGE_PREFIX = "com.bn.NobleJS.setImmediate" + Math.random();
function isStringAndStartsWith(string, putativeStart) {
return typeof string === "string" && string.substring(0, putativeStart.length) === putativeStart;
}
function onGlobalMessage(event) {
// This will catch all incoming messages (even from other windows!), so we need to try reasonably hard to
// avoid letting anyone else trick us into firing off. We test the origin is still this window, and that a
// (randomly generated) unpredictable identifying prefix is present.
if (event.source === global && isStringAndStartsWith(event.data, MESSAGE_PREFIX)) {
var handle = event.data.substring(MESSAGE_PREFIX.length);
tasks.runIfPresent(handle);
}
}
if (global.addEventListener) {
global.addEventListener("message", onGlobalMessage, false);
} else {
global.attachEvent("onmessage", onGlobalMessage);
}
attachTo.setImmediate = function () {
var handle = tasks.addFromSetImmediateArguments(arguments);
// Make `global` post a message to itself with the handle and identifying prefix, thus asynchronously
// invoking our onGlobalMessage listener above.
global.postMessage(MESSAGE_PREFIX + handle, "*");
return handle;
};
}
function installReadyStateChangeImplementation(attachTo) {
attachTo.setImmediate = function () {
var handle = tasks.addFromSetImmediateArguments(arguments);
// Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
// into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
var scriptEl = global.document.createElement("script");
scriptEl.onreadystatechange = function () {
tasks.runIfPresent(handle);
scriptEl.onreadystatechange = null;
scriptEl.parentNode.removeChild(scriptEl);
scriptEl = null;
};
global.document.documentElement.appendChild(scriptEl);
return handle;
};
}
function installSetTimeoutImplementation(attachTo) {
attachTo.setImmediate = function () {
var handle = tasks.addFromSetImmediateArguments(arguments);
global.setTimeout(function () {
tasks.runIfPresent(handle);
}, 0);
return handle;
};
}
if (!global.setImmediate) {
// If supported, we should attach to the prototype of global, since that is where setTimeout et al. live.
var attachTo = typeof Object.getPrototypeOf === "function" && "setTimeout" in Object.getPrototypeOf(global) ?
Object.getPrototypeOf(global)
: global;
if (canUseNextTick()) {
// For Node.js before 0.9
installNextTickImplementation(attachTo);
} else if (canUsePostMessage()) {
// For non-IE10 modern browsers
installPostMessageImplementation(attachTo);
} else if (canUseMessageChannel()) {
// For web workers, where supported
installMessageChannelImplementation(attachTo);
} else if (canUseReadyStateChange()) {
// For IE 6–8
installReadyStateChangeImplementation(attachTo);
} else {
// For older browsers
installSetTimeoutImplementation(attachTo);
}
attachTo.clearImmediate = tasks.remove;
}
}(typeof global === "object" && global ? global : this));
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment