Merge branch 'master' into alerting_definitions

This commit is contained in:
Torkel Ödegaard 2016-06-09 10:45:16 +02:00
commit dee5f582d7
14 changed files with 373 additions and 347 deletions

View File

@ -10,6 +10,7 @@
* **InfluxDB**: Add spread function, closes [#5211](https://github.com/grafana/grafana/issues/5211)
* **Scripts**: Use restart instead of start for deb package script, closes [#5282](https://github.com/grafana/grafana/pull/5282)
* **Logging**: Moved to structured logging lib, and moved to component specific level filters via config file, closes [#4590](https://github.com/grafana/grafana/issues/4590)
* **Search**: Add search limit query parameter, closes [#5292](https://github.com/grafana/grafana/pull/5292)
## Breaking changes
* **Logging** : Changed default logging output format (now structured into message, and key value pairs, with logger key acting as component). You can also no change in config to json log ouput.

View File

@ -44,6 +44,7 @@ func searchHandler(query *Query) error {
IsStarred: query.IsStarred,
OrgId: query.OrgId,
DashboardIds: query.DashboardIds,
Limit: query.Limit,
}
if err := bus.Dispatch(&dashQuery); err != nil {

View File

@ -42,6 +42,7 @@ type FindPersistedDashboardsQuery struct {
UserId int64
IsStarred bool
DashboardIds []int
Limit int
Result HitList
}

View File

@ -123,6 +123,11 @@ type DashboardSearchProjection struct {
}
func SearchDashboards(query *search.FindPersistedDashboardsQuery) error {
limit := query.Limit
if limit == 0 {
limit = 1000
}
var sql bytes.Buffer
params := make([]interface{}, 0)
@ -165,7 +170,8 @@ func SearchDashboards(query *search.FindPersistedDashboardsQuery) error {
params = append(params, "%"+query.Title+"%")
}
sql.WriteString(fmt.Sprintf(" ORDER BY dashboard.title ASC LIMIT 1000"))
sql.WriteString(fmt.Sprintf(" ORDER BY dashboard.title ASC LIMIT ?"))
params = append(params, limit)
var res []DashboardSearchProjection

View File

@ -0,0 +1,123 @@
///<reference path="../../../headers/common.d.ts" />
import _ from 'lodash';
export class QueryPartDef {
type: string;
params: any[];
defaultParams: any[];
renderer: any;
category: any;
addStrategy: any;
constructor(options: any) {
this.type = options.type;
this.params = options.params;
this.defaultParams = options.defaultParams;
this.renderer = options.renderer;
this.category = options.category;
this.addStrategy = options.addStrategy;
}
}
export class QueryPart {
part: any;
def: QueryPartDef;
params: any[];
text: string;
constructor(part: any, def: any) {
this.part = part;
this.def = def;
if (!this.def) {
throw {message: 'Could not find query part ' + part.type};
}
part.params = part.params || _.clone(this.def.defaultParams);
this.params = part.params;
this.updateText();
}
render(innerExpr: string) {
return this.def.renderer(this, innerExpr);
}
hasMultipleParamsInString (strValue, index) {
if (strValue.indexOf(',') === -1) {
return false;
}
return this.def.params[index + 1] && this.def.params[index + 1].optional;
}
updateParam (strValue, index) {
// handle optional parameters
// if string contains ',' and next param is optional, split and update both
if (this.hasMultipleParamsInString(strValue, index)) {
_.each(strValue.split(','), function(partVal: string, idx) {
this.updateParam(partVal.trim(), idx);
}, this);
return;
}
if (strValue === '' && this.def.params[index].optional) {
this.params.splice(index, 1);
} else {
this.params[index] = strValue;
}
this.part.params = this.params;
this.updateText();
}
updateText() {
if (this.params.length === 0) {
this.text = this.def.type + '()';
return;
}
var text = this.def.type + '(';
text += this.params.join(', ');
text += ')';
this.text = text;
}
}
export function functionRenderer(part, innerExpr) {
var str = part.def.type + '(';
var parameters = _.map(part.params, (value, index) => {
var paramType = part.def.params[index];
if (paramType.type === 'time') {
if (value === 'auto') {
value = '$interval';
}
}
if (paramType.quote === 'single') {
return "'" + value + "'";
} else if (paramType.quote === 'double') {
return '"' + value + '"';
}
return value;
});
if (innerExpr) {
parameters.unshift(innerExpr);
}
return str + parameters.join(', ') + ')';
}
export function suffixRenderer(part, innerExpr) {
return innerExpr + ' ' + part.params[0];
}
export function identityRenderer(part, innerExpr) {
return part.params[0];
}
export function quotedIdentityRenderer(part, innerExpr) {
return '"' + part.params[0] + '"';
}

View File

@ -0,0 +1,183 @@
///<reference path="../../../headers/common.d.ts" />
import _ from 'lodash';
import $ from 'jquery';
import coreModule from 'app/core/core_module';
var template = `
<div class="tight-form-func-controls">
<span class="pointer fa fa-remove" ng-click="removeActionInternal()"></span>
</div>
<a ng-click="toggleControls()" class="query-part-name">{{part.def.type}}</a>
<span>(</span><span class="query-part-parameters"></span><span>)</span>
`;
/** @ngInject */
export function queryPartEditorDirective($compile, templateSrv) {
var paramTemplate = '<input type="text" style="display:none"' +
' class="input-mini tight-form-func-param"></input>';
return {
restrict: 'E',
template: template,
scope: {
part: "=",
removeAction: "&",
partUpdated: "&",
getOptions: "&",
},
link: function postLink($scope, elem) {
var part = $scope.part;
var partDef = part.def;
var $paramsContainer = elem.find('.query-part-parameters');
var $controlsContainer = elem.find('.tight-form-func-controls');
function clickFuncParam(paramIndex) {
/*jshint validthis:true */
var $link = $(this);
var $input = $link.next();
$input.val(part.params[paramIndex]);
$input.css('width', ($link.width() + 16) + 'px');
$link.hide();
$input.show();
$input.focus();
$input.select();
var typeahead = $input.data('typeahead');
if (typeahead) {
$input.val('');
typeahead.lookup();
}
}
function inputBlur(paramIndex) {
/*jshint validthis:true */
var $input = $(this);
var $link = $input.prev();
var newValue = $input.val();
if (newValue !== '' || part.def.params[paramIndex].optional) {
$link.html(templateSrv.highlightVariablesAsHtml(newValue));
part.updateParam($input.val(), paramIndex);
$scope.$apply($scope.partUpdated);
}
$input.hide();
$link.show();
}
function inputKeyPress(paramIndex, e) {
/*jshint validthis:true */
if (e.which === 13) {
inputBlur.call(this, paramIndex);
}
}
function inputKeyDown() {
/*jshint validthis:true */
this.style.width = (3 + this.value.length) * 8 + 'px';
}
function addTypeahead($input, param, paramIndex) {
if (!param.options && !param.dynamicLookup) {
return;
}
var typeaheadSource = function (query, callback) {
if (param.options) { return param.options; }
$scope.$apply(function() {
$scope.getOptions().then(function(result) {
var dynamicOptions = _.map(result, function(op) { return op.value; });
callback(dynamicOptions);
});
});
};
$input.attr('data-provide', 'typeahead');
var options = param.options;
if (param.type === 'int') {
options = _.map(options, function(val) { return val.toString(); });
}
$input.typeahead({
source: typeaheadSource,
minLength: 0,
items: 1000,
updater: function (value) {
setTimeout(function() {
inputBlur.call($input[0], paramIndex);
}, 0);
return value;
}
});
var typeahead = $input.data('typeahead');
typeahead.lookup = function () {
this.query = this.$element.val() || '';
var items = this.source(this.query, $.proxy(this.process, this));
return items ? this.process(items) : items;
};
}
$scope.toggleControls = function() {
var targetDiv = elem.closest('.tight-form');
if (elem.hasClass('show-function-controls')) {
elem.removeClass('show-function-controls');
targetDiv.removeClass('has-open-function');
$controlsContainer.hide();
return;
}
elem.addClass('show-function-controls');
targetDiv.addClass('has-open-function');
$controlsContainer.show();
};
$scope.removeActionInternal = function() {
$scope.toggleControls();
$scope.removeAction();
};
function addElementsAndCompile() {
_.each(partDef.params, function(param, index) {
if (param.optional && part.params.length <= index) {
return;
}
if (index > 0) {
$('<span>, </span>').appendTo($paramsContainer);
}
var paramValue = templateSrv.highlightVariablesAsHtml(part.params[index]);
var $paramLink = $('<a class="graphite-func-param-link pointer">' + paramValue + '</a>');
var $input = $(paramTemplate);
$paramLink.appendTo($paramsContainer);
$input.appendTo($paramsContainer);
$input.blur(_.partial(inputBlur, index));
$input.keyup(inputKeyDown);
$input.keypress(_.partial(inputKeyPress, index));
$paramLink.click(_.partial(clickFuncParam, index));
addTypeahead($input, param, index);
});
}
function relink() {
$paramsContainer.empty();
addElementsAndCompile();
}
relink();
}
};
}
coreModule.directive('queryPartEditor', queryPartEditorDirective);

View File

@ -33,6 +33,7 @@ import {Emitter} from './utils/emitter';
import {layoutSelector} from './components/layout_selector/layout_selector';
import {switchDirective} from './components/switch';
import {dashboardSelector} from './components/dashboard_selector';
import {queryPartEditorDirective} from './components/query_part/query_part_editor';
import 'app/core/controllers/all';
import 'app/core/services/all';
import 'app/core/routes/routes';
@ -56,4 +57,5 @@ export {
Emitter,
appEvents,
dashboardSelector,
queryPartEditorDirective,
};

View File

@ -51,7 +51,7 @@
<span class="btn btn-primary btn-mini" ng-show="org.orgId === contextSrv.user.orgId">
Current
</span>
<a ng-click="setUsingOrg(org)" class="btn btn-inverse btn-mini" ng-show="org.orgId !== contextSrv.user.orgId">
<a ng-click="ctrl.setUsingOrg(org)" class="btn btn-inverse btn-mini" ng-show="org.orgId !== contextSrv.user.orgId">
Select
</a>
</td>

View File

@ -35,13 +35,13 @@
</div>
<div class="gf-form" ng-repeat="part in selectParts">
<influx-query-part-editor
<query-part-editor
class="gf-form-label query-part"
part="part"
remove-action="ctrl.removeSelectPart(selectParts, part)"
part-updated="ctrl.selectPartUpdated(selectParts, part)"
get-options="ctrl.getPartOptions(part)">
</influx-query-part-editor>
</query-part-editor>
</div>
<div class="gf-form">
@ -62,12 +62,12 @@
<span>GROUP BY</span>
</label>
<influx-query-part-editor
<query-part-editor
ng-repeat="part in ctrl.queryModel.groupByParts"
part="part"
class="gf-form-label query-part"
remove-action="ctrl.removeGroupByPart(part, $index)" part-updated="ctrl.refresh();" get-options="ctrl.getPartOptions(part)">
</influx-query-part-editor>
</query-part-editor>
</div>
<div class="gf-form">

View File

@ -1,5 +0,0 @@
<div class="tight-form-func-controls">
<span class="pointer fa fa-remove" ng-click="removeActionInternal()" ></span>
</div>
<a ng-click="toggleControls()" class="query-part-name">{{part.def.type}}</a><span>(</span><span class="query-part-parameters"></span><span>)</span>

View File

@ -1,8 +1,5 @@
///<reference path="../../../headers/common.d.ts" />
import './query_part_editor';
import './query_part_editor';
import angular from 'angular';
import _ from 'lodash';
import InfluxQueryBuilder from './query_builder';

View File

@ -1,6 +1,14 @@
///<reference path="../../../headers/common.d.ts" />
import _ from 'lodash';
import {
QueryPartDef,
QueryPart,
functionRenderer,
suffixRenderer,
identityRenderer,
quotedIdentityRenderer,
} from 'app/core/components/query_part/query_part';
var index = [];
var categories = {
@ -12,71 +20,26 @@ var categories = {
Fields: [],
};
function createPart(part): any {
var def = index[part.type];
if (!def) {
throw {message: 'Could not find query part ' + part.type};
}
return new QueryPart(part, def);
};
function register(options: any) {
index[options.type] = new QueryPartDef(options);
options.category.push(index[options.type]);
}
var groupByTimeFunctions = [];
class QueryPartDef {
type: string;
params: any[];
defaultParams: any[];
renderer: any;
category: any;
addStrategy: any;
constructor(options: any) {
this.type = options.type;
this.params = options.params;
this.defaultParams = options.defaultParams;
this.renderer = options.renderer;
this.category = options.category;
this.addStrategy = options.addStrategy;
}
static register(options: any) {
index[options.type] = new QueryPartDef(options);
options.category.push(index[options.type]);
}
}
function functionRenderer(part, innerExpr) {
var str = part.def.type + '(';
var parameters = _.map(part.params, (value, index) => {
var paramType = part.def.params[index];
if (paramType.type === 'time') {
if (value === 'auto') {
value = '$interval';
}
}
if (paramType.quote === 'single') {
return "'" + value + "'";
} else if (paramType.quote === 'double') {
return '"' + value + '"';
}
return value;
});
if (innerExpr) {
parameters.unshift(innerExpr);
}
return str + parameters.join(', ') + ')';
}
function aliasRenderer(part, innerExpr) {
return innerExpr + ' AS ' + '"' + part.params[0] + '"';
}
function suffixRenderer(part, innerExpr) {
return innerExpr + ' ' + part.params[0];
}
function identityRenderer(part, innerExpr) {
return part.params[0];
}
function quotedIdentityRenderer(part, innerExpr) {
return '"' + part.params[0] + '"';
}
function fieldRenderer(part, innerExpr) {
if (part.params[0] === '*') {
return '*';
@ -149,13 +112,13 @@ function addAliasStrategy(selectParts, partModel) {
function addFieldStrategy(selectParts, partModel, query) {
// copy all parts
var parts = _.map(selectParts, function(part: any) {
return new QueryPart({type: part.def.type, params: _.clone(part.params)});
return createPart({type: part.def.type, params: _.clone(part.params)});
});
query.selectModels.push(parts);
}
QueryPartDef.register({
register({
type: 'field',
addStrategy: addFieldStrategy,
category: categories.Fields,
@ -165,7 +128,7 @@ QueryPartDef.register({
});
// Aggregations
QueryPartDef.register({
register({
type: 'count',
addStrategy: replaceAggregationAddStrategy,
category: categories.Aggregations,
@ -174,7 +137,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
QueryPartDef.register({
register({
type: 'distinct',
addStrategy: replaceAggregationAddStrategy,
category: categories.Aggregations,
@ -183,7 +146,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
QueryPartDef.register({
register({
type: 'integral',
addStrategy: replaceAggregationAddStrategy,
category: categories.Aggregations,
@ -192,7 +155,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
QueryPartDef.register({
register({
type: 'mean',
addStrategy: replaceAggregationAddStrategy,
category: categories.Aggregations,
@ -201,7 +164,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
QueryPartDef.register({
register({
type: 'median',
addStrategy: replaceAggregationAddStrategy,
category: categories.Aggregations,
@ -210,7 +173,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
QueryPartDef.register({
register({
type: 'sum',
addStrategy: replaceAggregationAddStrategy,
category: categories.Aggregations,
@ -221,7 +184,7 @@ QueryPartDef.register({
// transformations
QueryPartDef.register({
register({
type: 'derivative',
addStrategy: addTransformationStrategy,
category: categories.Transformations,
@ -230,7 +193,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
QueryPartDef.register({
register({
type: 'spread',
addStrategy: addTransformationStrategy,
category: categories.Transformations,
@ -239,7 +202,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
QueryPartDef.register({
register({
type: 'non_negative_derivative',
addStrategy: addTransformationStrategy,
category: categories.Transformations,
@ -248,7 +211,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
QueryPartDef.register({
register({
type: 'difference',
addStrategy: addTransformationStrategy,
category: categories.Transformations,
@ -257,7 +220,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
QueryPartDef.register({
register({
type: 'moving_average',
addStrategy: addTransformationStrategy,
category: categories.Transformations,
@ -266,7 +229,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
QueryPartDef.register({
register({
type: 'stddev',
addStrategy: addTransformationStrategy,
category: categories.Transformations,
@ -275,7 +238,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
QueryPartDef.register({
register({
type: 'time',
category: groupByTimeFunctions,
params: [{ name: "interval", type: "time", options: ['auto', '1s', '10s', '1m', '5m', '10m', '15m', '1h'] }],
@ -283,7 +246,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
QueryPartDef.register({
register({
type: 'fill',
category: groupByTimeFunctions,
params: [{ name: "fill", type: "string", options: ['none', 'null', '0', 'previous'] }],
@ -292,7 +255,7 @@ QueryPartDef.register({
});
// Selectors
QueryPartDef.register({
register({
type: 'bottom',
addStrategy: replaceAggregationAddStrategy,
category: categories.Selectors,
@ -301,7 +264,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
QueryPartDef.register({
register({
type: 'first',
addStrategy: replaceAggregationAddStrategy,
category: categories.Selectors,
@ -310,7 +273,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
QueryPartDef.register({
register({
type: 'last',
addStrategy: replaceAggregationAddStrategy,
category: categories.Selectors,
@ -319,7 +282,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
QueryPartDef.register({
register({
type: 'max',
addStrategy: replaceAggregationAddStrategy,
category: categories.Selectors,
@ -328,7 +291,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
QueryPartDef.register({
register({
type: 'min',
addStrategy: replaceAggregationAddStrategy,
category: categories.Selectors,
@ -337,7 +300,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
QueryPartDef.register({
register({
type: 'percentile',
addStrategy: replaceAggregationAddStrategy,
category: categories.Selectors,
@ -346,7 +309,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
QueryPartDef.register({
register({
type: 'top',
addStrategy: replaceAggregationAddStrategy,
category: categories.Selectors,
@ -355,7 +318,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
QueryPartDef.register({
register({
type: 'tag',
category: groupByTimeFunctions,
params: [{name: 'tag', type: 'string', dynamicLookup: true}],
@ -363,7 +326,7 @@ QueryPartDef.register({
renderer: fieldRenderer,
});
QueryPartDef.register({
register({
type: 'math',
addStrategy: addMathStrategy,
category: categories.Math,
@ -372,7 +335,7 @@ QueryPartDef.register({
renderer: suffixRenderer,
});
QueryPartDef.register({
register({
type: 'alias',
addStrategy: addAliasStrategy,
category: categories.Aliasing,
@ -382,74 +345,9 @@ QueryPartDef.register({
renderer: aliasRenderer,
});
class QueryPart {
part: any;
def: QueryPartDef;
params: any[];
text: string;
constructor(part: any) {
this.part = part;
this.def = index[part.type];
if (!this.def) {
throw {message: 'Could not find query part ' + part.type};
}
part.params = part.params || _.clone(this.def.defaultParams);
this.params = part.params;
this.updateText();
}
render(innerExpr: string) {
return this.def.renderer(this, innerExpr);
}
hasMultipleParamsInString (strValue, index) {
if (strValue.indexOf(',') === -1) {
return false;
}
return this.def.params[index + 1] && this.def.params[index + 1].optional;
}
updateParam (strValue, index) {
// handle optional parameters
// if string contains ',' and next param is optional, split and update both
if (this.hasMultipleParamsInString(strValue, index)) {
_.each(strValue.split(','), function(partVal: string, idx) {
this.updateParam(partVal.trim(), idx);
}, this);
return;
}
if (strValue === '' && this.def.params[index].optional) {
this.params.splice(index, 1);
} else {
this.params[index] = strValue;
}
this.part.params = this.params;
this.updateText();
}
updateText() {
if (this.params.length === 0) {
this.text = this.def.type + '()';
return;
}
var text = this.def.type + '(';
text += this.params.join(', ');
text += ')';
this.text = text;
}
}
export default {
create: function(part): any {
return new QueryPart(part);
},
create: createPart,
getCategories: function() {
return categories;
}

View File

@ -1,178 +0,0 @@
define([
'angular',
'lodash',
'jquery',
],
function (angular, _, $) {
'use strict';
angular
.module('grafana.directives')
.directive('influxQueryPartEditor', function($compile, templateSrv) {
var paramTemplate = '<input type="text" style="display:none"' +
' class="input-mini tight-form-func-param"></input>';
return {
restrict: 'E',
templateUrl: 'public/app/plugins/datasource/influxdb/partials/query_part.html',
scope: {
part: "=",
removeAction: "&",
partUpdated: "&",
getOptions: "&",
},
link: function postLink($scope, elem) {
var part = $scope.part;
var partDef = part.def;
var $paramsContainer = elem.find('.query-part-parameters');
var $controlsContainer = elem.find('.tight-form-func-controls');
function clickFuncParam(paramIndex) {
/*jshint validthis:true */
var $link = $(this);
var $input = $link.next();
$input.val(part.params[paramIndex]);
$input.css('width', ($link.width() + 16) + 'px');
$link.hide();
$input.show();
$input.focus();
$input.select();
var typeahead = $input.data('typeahead');
if (typeahead) {
$input.val('');
typeahead.lookup();
}
}
function inputBlur(paramIndex) {
/*jshint validthis:true */
var $input = $(this);
var $link = $input.prev();
var newValue = $input.val();
if (newValue !== '' || part.def.params[paramIndex].optional) {
$link.html(templateSrv.highlightVariablesAsHtml(newValue));
part.updateParam($input.val(), paramIndex);
$scope.$apply($scope.partUpdated);
}
$input.hide();
$link.show();
}
function inputKeyPress(paramIndex, e) {
/*jshint validthis:true */
if(e.which === 13) {
inputBlur.call(this, paramIndex);
}
}
function inputKeyDown() {
/*jshint validthis:true */
this.style.width = (3 + this.value.length) * 8 + 'px';
}
function addTypeahead($input, param, paramIndex) {
if (!param.options && !param.dynamicLookup) {
return;
}
var typeaheadSource = function (query, callback) {
if (param.options) { return param.options; }
$scope.$apply(function() {
$scope.getOptions().then(function(result) {
var dynamicOptions = _.map(result, function(op) { return op.value; });
callback(dynamicOptions);
});
});
};
$input.attr('data-provide', 'typeahead');
var options = param.options;
if (param.type === 'int') {
options = _.map(options, function(val) { return val.toString(); });
}
$input.typeahead({
source: typeaheadSource,
minLength: 0,
items: 1000,
updater: function (value) {
setTimeout(function() {
inputBlur.call($input[0], paramIndex);
}, 0);
return value;
}
});
var typeahead = $input.data('typeahead');
typeahead.lookup = function () {
this.query = this.$element.val() || '';
var items = this.source(this.query, $.proxy(this.process, this));
return items ? this.process(items) : items;
};
}
$scope.toggleControls = function() {
var targetDiv = elem.closest('.tight-form');
if (elem.hasClass('show-function-controls')) {
elem.removeClass('show-function-controls');
targetDiv.removeClass('has-open-function');
$controlsContainer.hide();
return;
}
elem.addClass('show-function-controls');
targetDiv.addClass('has-open-function');
$controlsContainer.show();
};
$scope.removeActionInternal = function() {
$scope.toggleControls();
$scope.removeAction();
};
function addElementsAndCompile() {
_.each(partDef.params, function(param, index) {
if (param.optional && part.params.length <= index) {
return;
}
if (index > 0) {
$('<span>, </span>').appendTo($paramsContainer);
}
var paramValue = templateSrv.highlightVariablesAsHtml(part.params[index]);
var $paramLink = $('<a class="graphite-func-param-link pointer">' + paramValue + '</a>');
var $input = $(paramTemplate);
$paramLink.appendTo($paramsContainer);
$input.appendTo($paramsContainer);
$input.blur(_.partial(inputBlur, index));
$input.keyup(inputKeyDown);
$input.keypress(_.partial(inputKeyPress, index));
$paramLink.click(_.partial(clickFuncParam, index));
addTypeahead($input, param, index);
});
}
function relink() {
$paramsContainer.empty();
addElementsAndCompile();
}
relink();
}
};
});
});

View File

@ -403,10 +403,7 @@ function (angular, _, dateMath) {
} else {
return _.findIndex(options.targets, function(target) {
if (target.filters && target.filters.length > 0) {
return target.metric === metricData.metric &&
_.all(target.filters, function(filter) {
return filter.tagk === interpolatedTagValue === "*";
});
return target.metric === metricData.metric;
} else {
return target.metric === metricData.metric &&
_.all(target.tags, function(tagV, tagK) {