mirror of
https://github.com/grafana/grafana.git
synced 2026-08-04 10:33:21 -05:00
* Add UMLs * Add rendered diagrams * Move QueryCtrl to flux * Remove redundant param in the reducer * Use named imports for lodash and fix typing for GraphiteTagOperator * Add missing async/await * Extract providers to a separate file * Clean up async await * Rename controller functions back to main * Simplify creating actions * Re-order controller functions * Separate helpers from actions * Rename vars * Simplify helpers * Move controller methods to state reducers * Remove docs (they are added in design doc) * Move actions.ts to state folder * Add docs * Add old methods stubs for easier review * Check how state dependencies will be mapped * Rename state to store * Rename state to store * Rewrite spec tests for Graphite Query Controller * Update docs * Update docs * Update public/app/plugins/datasource/graphite/state/helpers.ts Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Update public/app/plugins/datasource/graphite/state/helpers.ts Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Update public/app/plugins/datasource/graphite/state/helpers.ts Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Update public/app/plugins/datasource/graphite/state/providers.ts Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Update public/app/plugins/datasource/graphite/state/providers.ts Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Update public/app/plugins/datasource/graphite/state/providers.ts Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Update public/app/plugins/datasource/graphite/state/providers.ts Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Update public/app/plugins/datasource/graphite/state/providers.ts Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Update public/app/plugins/datasource/graphite/state/providers.ts Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Add more type definitions * Load function definitions before parsing the target on initial load * Change targetChanged to updateQuery to avoid mutating state directly It's also needed for extra refresh/runQuery execution as handleTargetChanged doesn't handle changing the raw query * Fix updating query after adding a function * Simplify updating function params * Remove redundant awaits * Use redux Action * Use more specific type for GraphiteTag Co-authored-by: Giordano Ricci <me@giordanoricci.com>
257 lines
7.5 KiB
TypeScript
257 lines
7.5 KiB
TypeScript
import { assign, clone, each, last, map, partial } from 'lodash';
|
|
import $ from 'jquery';
|
|
import coreModule from 'app/core/core_module';
|
|
import { TemplateSrv } from 'app/features/templating/template_srv';
|
|
import { actions } from './state/actions';
|
|
|
|
/** @ngInject */
|
|
export function graphiteFuncEditor($compile: any, templateSrv: TemplateSrv) {
|
|
const funcSpanTemplate = `
|
|
<function-editor
|
|
func="func"
|
|
onRemove="ctrl.handleRemoveFunction"
|
|
onMoveLeft="ctrl.handleMoveLeft"
|
|
onMoveRight="ctrl.handleMoveRight">
|
|
</function-editor>
|
|
<span>(</span>
|
|
`;
|
|
const paramTemplate =
|
|
'<input type="text" style="display:none"' + ' class="input-small tight-form-func-param"></input>';
|
|
|
|
return {
|
|
restrict: 'A',
|
|
link: function postLink($scope: any, elem: JQuery) {
|
|
const $funcLink = $(funcSpanTemplate);
|
|
const ctrl = $scope.ctrl;
|
|
const func = $scope.func;
|
|
let scheduledRelink = false;
|
|
let paramCountAtLink = 0;
|
|
let cancelBlur: any = null;
|
|
|
|
ctrl.handleRemoveFunction = (func: any) => {
|
|
ctrl.dispatch(actions.removeFunction({ func }));
|
|
};
|
|
|
|
ctrl.handleMoveLeft = (func: any) => {
|
|
ctrl.dispatch(actions.moveFunction({ func, offset: -1 }));
|
|
};
|
|
|
|
ctrl.handleMoveRight = (func: any) => {
|
|
ctrl.dispatch(actions.moveFunction({ func, offset: 1 }));
|
|
};
|
|
|
|
function clickFuncParam(this: any, paramIndex: any) {
|
|
const $link = $(this);
|
|
const $comma = $link.prev('.comma');
|
|
const $input = $link.next();
|
|
|
|
$input.val(func.params[paramIndex]);
|
|
|
|
$comma.removeClass('query-part__last');
|
|
$link.hide();
|
|
$input.show();
|
|
$input.focus();
|
|
$input.select();
|
|
|
|
const typeahead = $input.data('typeahead');
|
|
if (typeahead) {
|
|
$input.val('');
|
|
typeahead.lookup();
|
|
}
|
|
}
|
|
|
|
function scheduledRelinkIfNeeded() {
|
|
if (paramCountAtLink === func.params.length) {
|
|
return;
|
|
}
|
|
|
|
if (!scheduledRelink) {
|
|
scheduledRelink = true;
|
|
setTimeout(() => {
|
|
relink();
|
|
scheduledRelink = false;
|
|
}, 200);
|
|
}
|
|
}
|
|
|
|
function paramDef(index: number) {
|
|
if (index < func.def.params.length) {
|
|
return func.def.params[index];
|
|
}
|
|
if ((last(func.def.params) as any).multiple) {
|
|
return assign({}, last(func.def.params), { optional: true });
|
|
}
|
|
return {};
|
|
}
|
|
|
|
function switchToLink(inputElem: HTMLElement, paramIndex: any) {
|
|
const $input = $(inputElem);
|
|
|
|
clearTimeout(cancelBlur);
|
|
cancelBlur = null;
|
|
|
|
const $link = $input.prev();
|
|
const $comma = $link.prev('.comma');
|
|
const newValue = $input.val() as any;
|
|
|
|
// remove optional empty params
|
|
if (newValue !== '' || paramDef(paramIndex).optional) {
|
|
func.updateParam(newValue, paramIndex);
|
|
$link.html(newValue ? templateSrv.highlightVariablesAsHtml(newValue) : ' ');
|
|
}
|
|
|
|
scheduledRelinkIfNeeded();
|
|
|
|
$scope.$apply(() => {
|
|
// WIP: at the moment function params are mutated directly by func_editor
|
|
// after migrating to react it will be done by passing param value to
|
|
// updateFunctionParam action
|
|
ctrl.dispatch(actions.updateFunctionParam({ func }));
|
|
});
|
|
|
|
if ($link.hasClass('query-part__last') && newValue === '') {
|
|
$comma.addClass('query-part__last');
|
|
} else {
|
|
$link.removeClass('query-part__last');
|
|
}
|
|
|
|
$input.hide();
|
|
$link.show();
|
|
}
|
|
|
|
// this = input element
|
|
function inputBlur(this: any, paramIndex: any) {
|
|
const inputElem = this;
|
|
// happens long before the click event on the typeahead options
|
|
// need to have long delay because the blur
|
|
cancelBlur = setTimeout(() => {
|
|
switchToLink(inputElem, paramIndex);
|
|
}, 200);
|
|
}
|
|
|
|
function inputKeyPress(this: any, paramIndex: any, e: any) {
|
|
if (e.which === 13) {
|
|
$(this).blur();
|
|
}
|
|
}
|
|
|
|
function inputKeyDown(this: any) {
|
|
this.style.width = (3 + this.value.length) * 8 + 'px';
|
|
}
|
|
|
|
function addTypeahead($input: any, paramIndex: any) {
|
|
$input.attr('data-provide', 'typeahead');
|
|
|
|
let options = paramDef(paramIndex).options;
|
|
if (paramDef(paramIndex).type === 'int') {
|
|
options = map(options, (val) => {
|
|
return val.toString();
|
|
});
|
|
}
|
|
|
|
$input.typeahead({
|
|
source: options,
|
|
minLength: 0,
|
|
items: 20,
|
|
updater: (value: any) => {
|
|
$input.val(value);
|
|
switchToLink($input[0], paramIndex);
|
|
return value;
|
|
},
|
|
});
|
|
|
|
const typeahead = $input.data('typeahead');
|
|
typeahead.lookup = function () {
|
|
this.query = this.$element.val() || '';
|
|
return this.process(this.source);
|
|
};
|
|
}
|
|
|
|
function addElementsAndCompile() {
|
|
$funcLink.appendTo(elem);
|
|
|
|
if (func.def.unknown) {
|
|
elem.addClass('unknown-function');
|
|
}
|
|
|
|
const defParams: any = clone(func.def.params);
|
|
const lastParam: any = last(func.def.params);
|
|
|
|
while (func.params.length >= defParams.length && lastParam && lastParam.multiple) {
|
|
defParams.push(assign({}, lastParam, { optional: true }));
|
|
}
|
|
|
|
each(defParams, (param: any, index: number) => {
|
|
if (param.optional && func.params.length < index) {
|
|
return false;
|
|
}
|
|
|
|
let paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]);
|
|
const hasValue = paramValue !== null && paramValue !== undefined && paramValue !== '';
|
|
const last = index >= func.params.length - 1 && param.optional && !hasValue;
|
|
let linkClass = 'query-part__link';
|
|
|
|
if (last) {
|
|
linkClass += ' query-part__last';
|
|
}
|
|
|
|
if (last && param.multiple) {
|
|
paramValue = '+';
|
|
} else if (!hasValue) {
|
|
// for params with no value default to param name
|
|
paramValue = param.name;
|
|
linkClass += ' query-part__link--no-value';
|
|
}
|
|
|
|
if (index > 0) {
|
|
$('<span class="comma' + (last ? ' query-part__last' : '') + '">, </span>').appendTo(elem);
|
|
}
|
|
|
|
const $paramLink = $(`<a ng-click="" class="${linkClass}">${paramValue}</a>`);
|
|
const $input = $(paramTemplate);
|
|
$input.attr('placeholder', param.name);
|
|
|
|
paramCountAtLink++;
|
|
|
|
$paramLink.appendTo(elem);
|
|
$input.appendTo(elem);
|
|
|
|
$input.blur(partial(inputBlur, index));
|
|
$input.keyup(inputKeyDown);
|
|
$input.keypress(partial(inputKeyPress, index));
|
|
$paramLink.click(partial(clickFuncParam, index));
|
|
|
|
if (param.options) {
|
|
addTypeahead($input, index);
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
$('<span>)</span>').appendTo(elem);
|
|
|
|
$compile(elem.contents())($scope);
|
|
}
|
|
|
|
function ifJustAddedFocusFirstParam() {
|
|
if ($scope.func.added) {
|
|
$scope.func.added = false;
|
|
setTimeout(() => {
|
|
elem.find('.query-part__link').first().click();
|
|
}, 10);
|
|
}
|
|
}
|
|
|
|
function relink() {
|
|
elem.children().remove();
|
|
addElementsAndCompile();
|
|
ifJustAddedFocusFirstParam();
|
|
}
|
|
|
|
relink();
|
|
},
|
|
};
|
|
}
|
|
|
|
coreModule.directive('graphiteFuncEditor', graphiteFuncEditor);
|