Skip to content

Commit

Permalink
support for loading function definitions from graphite
Browse files Browse the repository at this point in the history
  • Loading branch information
DanCech committed Jan 16, 2018
1 parent 307b419 commit 3a4e051
Show file tree
Hide file tree
Showing 14 changed files with 658 additions and 358 deletions.
1 change: 0 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 120
insert_final_newline = true

[*.go]
indent_style = tab
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@
"clipboard": "^1.7.1",
"d3": "^4.11.0",
"d3-scale-chromatic": "^1.1.1",
"eventemitter3": "^2.0.2",
"eventemitter3": "^2.0.3",
"file-saver": "^1.3.3",
"jquery": "^3.2.1",
"lodash": "^4.17.4",
Expand All @@ -154,6 +154,7 @@
"react-select": "^1.1.0",
"react-sizeme": "^2.3.6",
"remarkable": "^1.7.1",
"rst2html": "github:thoward/rst2html#d6e2f21",
"rxjs": "^5.4.3",
"tether": "^1.4.0",
"tether-drop": "https://github.com/torkelo/drop",
Expand Down
140 changes: 69 additions & 71 deletions public/app/plugins/datasource/graphite/add_graphite_func.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@ define([
'angular',
'lodash',
'jquery',
'./gfunc',
],
function (angular, _, $, gfunc) {
function (angular, _, $) {
'use strict';

gfunc = gfunc.default;

angular
.module('grafana.directives')
.directive('graphiteAddFunc', function($compile) {
Expand All @@ -23,91 +20,92 @@ function (angular, _, $, gfunc) {
return {
link: function($scope, elem) {
var ctrl = $scope.ctrl;
var graphiteVersion = ctrl.datasource.graphiteVersion;
var categories = gfunc.getCategories(graphiteVersion);
var allFunctions = getAllFunctionNames(categories);

$scope.functionMenu = createFunctionDropDownMenu(categories);

var $input = $(inputTemplate);
var $button = $(buttonTemplate);

$input.appendTo(elem);
$button.appendTo(elem);

$input.attr('data-provide', 'typeahead');
$input.typeahead({
source: allFunctions,
minLength: 1,
items: 10,
updater: function (value) {
var funcDef = gfunc.getFuncDef(value);
if (!funcDef) {
// try find close match
value = value.toLowerCase();
funcDef = _.find(allFunctions, function(funcName) {
return funcName.toLowerCase().indexOf(value) === 0;
ctrl.datasource.getFuncDefs().then(function(funcDefs) {
var allFunctions = _.map(funcDefs, 'name').sort();

$scope.functionMenu = createFunctionDropDownMenu(funcDefs);

$input.attr('data-provide', 'typeahead');
$input.typeahead({
source: allFunctions,
minLength: 1,
items: 10,
updater: function (value) {
var funcDef = ctrl.datasource.getFuncDef(value);
if (!funcDef) {
// try find close match
value = value.toLowerCase();
funcDef = _.find(allFunctions, function(funcName) {
return funcName.toLowerCase().indexOf(value) === 0;
});

if (!funcDef) { return; }
}

$scope.$apply(function() {
ctrl.addFunction(funcDef);
});

if (!funcDef) { return; }
$input.trigger('blur');
return '';
}

$scope.$apply(function() {
ctrl.addFunction(funcDef);
});

$input.trigger('blur');
return '';
}
});

$button.click(function() {
$button.hide();
$input.show();
$input.focus();
});

$input.keyup(function() {
elem.toggleClass('open', $input.val() === '');
});

$button.click(function() {
$button.hide();
$input.show();
$input.focus();
});

$input.keyup(function() {
elem.toggleClass('open', $input.val() === '');
});

$input.blur(function() {
// clicking the function dropdown menu wont
// work if you remove class at once
setTimeout(function() {
$input.val('');
$input.hide();
$button.show();
elem.removeClass('open');
}, 200);
});

$compile(elem.contents())($scope);
});

$input.blur(function() {
// clicking the function dropdown menu wont
// work if you remove class at once
setTimeout(function() {
$input.val('');
$input.hide();
$button.show();
elem.removeClass('open');
}, 200);
});

$compile(elem.contents())($scope);
}
};
});

function getAllFunctionNames(categories) {
return _.reduce(categories, function(list, category) {
_.each(category, function(func) {
list.push(func.name);
});
return list;
}, []);
}

function createFunctionDropDownMenu(categories) {
return _.map(categories, function(list, category) {
var submenu = _.map(list, function(value) {
return {
text: value.name,
click: "ctrl.addFunction('" + value.name + "')",
};
function createFunctionDropDownMenu(funcDefs) {
var categories = {};

_.forEach(funcDefs, function(funcDef) {
if (!funcDef.category) {
return;
}
if (!categories[funcDef.category]) {
categories[funcDef.category] = [];
}
categories[funcDef.category].push({
text: funcDef.name,
click: "ctrl.addFunction('" + funcDef.name + "')",
});
});

return _.sortBy(_.map(categories, function(submenu, category) {
return {
text: category,
submenu: submenu
submenu: _.sortBy(submenu, 'text')
};
});
}), 'text');
}
});
125 changes: 125 additions & 0 deletions public/app/plugins/datasource/graphite/datasource.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import _ from 'lodash';
import * as dateMath from 'app/core/utils/datemath';
import { isVersionGtOrEq, SemVersion } from 'app/core/utils/version';
import gfunc from './gfunc';

/** @ngInject */
export function GraphiteDatasource(instanceSettings, $q, backendSrv, templateSrv) {
Expand All @@ -12,6 +13,7 @@ export function GraphiteDatasource(instanceSettings, $q, backendSrv, templateSrv
this.cacheTimeout = instanceSettings.cacheTimeout;
this.withCredentials = instanceSettings.withCredentials;
this.render_method = instanceSettings.render_method || 'POST';
this.funcDefs = null;

this.getQueryOptionsInfo = function() {
return {
Expand Down Expand Up @@ -347,6 +349,125 @@ export function GraphiteDatasource(instanceSettings, $q, backendSrv, templateSrv
});
};

this.createFuncInstance = function(funcDef, options?) {
return gfunc.createFuncInstance(funcDef, options, this.funcDefs);
};

this.getFuncDef = function(name) {
return gfunc.getFuncDef(name, this.funcDefs);
};

this.getFuncDefs = function() {
let self = this;

if (self.funcDefs !== null) {
return Promise.resolve(self.funcDefs);
}

if (!supportsFunctionIndex(self.graphiteVersion)) {
self.funcDefs = gfunc.getFuncDefs(self.graphiteVersion);
return Promise.resolve(self.funcDefs);
}

let httpOptions = {
method: 'GET',
url: '/functions',
};

return self
.doGraphiteRequest(httpOptions)
.then(results => {
if (results.status !== 200 || typeof results.data !== 'object') {
self.funcDefs = gfunc.getFuncDefs(self.graphiteVersion);
return Promise.resolve(self.funcDefs);
}

self.funcDefs = {};
_.forEach(results.data || {}, (funcDef, funcName) => {
// skip graphite graph functions
if (funcDef.group === 'Graph') {
return;
}

var func = {
name: funcDef.name,
description: funcDef.description,
category: funcDef.group,
params: [],
defaultParams: [],
fake: false,
};

// get rid of the first "seriesList" param
if (/^seriesLists?$/.test(_.get(funcDef, 'params[0].type', ''))) {
// handle functions that accept multiple seriesLists
// we leave the param in place but mark it optional, so users can add more series if they wish
if (funcDef.params[0].multiple) {
funcDef.params[0].required = false;
// otherwise chop off the first param, it'll be handled separately
} else {
funcDef.params.shift();
}
// tag function as fake
} else {
func.fake = true;
}

_.forEach(funcDef.params, rawParam => {
var param = {
name: rawParam.name,
type: 'string',
optional: !rawParam.required,
multiple: !!rawParam.multiple,
options: undefined,
};

if (rawParam.default !== undefined) {
func.defaultParams.push(_.toString(rawParam.default));
} else if (rawParam.suggestions) {
func.defaultParams.push(_.toString(rawParam.suggestions[0]));
} else {
func.defaultParams.push('');
}

if (rawParam.type === 'boolean') {
param.type = 'boolean';
param.options = ['true', 'false'];
} else if (rawParam.type === 'integer') {
param.type = 'int';
} else if (rawParam.type === 'float') {
param.type = 'float';
} else if (rawParam.type === 'node') {
param.type = 'node';
param.options = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'];
} else if (rawParam.type === 'nodeOrTag') {
param.type = 'node_or_tag';
param.options = ['name', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'];
} else if (rawParam.type === 'intOrInterval') {
param.type = 'int_or_interval';
} else if (rawParam.type === 'seriesList') {
param.type = 'value_or_series';
}

if (rawParam.options) {
param.options = _.map(rawParam.options, _.toString);
} else if (rawParam.suggestions) {
param.options = _.map(rawParam.suggestions, _.toString);
}

func.params.push(param);
});

self.funcDefs[funcName] = func;
});
return self.funcDefs;
})
.catch(err => {
self.funcDefs = gfunc.getFuncDefs(self.graphiteVersion);
return self.funcDefs;
});
};

this.testDatasource = function() {
return this.metricFindQuery('*').then(function() {
return { status: 'success', message: 'Data source is working' };
Expand Down Expand Up @@ -440,3 +561,7 @@ export function GraphiteDatasource(instanceSettings, $q, backendSrv, templateSrv
function supportsTags(version: string): boolean {
return isVersionGtOrEq(version, '1.1');
}

function supportsFunctionIndex(version: string): boolean {
return isVersionGtOrEq(version, '1.1');
}
Loading

0 comments on commit 3a4e051

Please sign in to comment.