From 83b9db51e3434466cd2a01d06549ad95c44a798b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 14 Sep 2016 17:36:28 +0200 Subject: [PATCH] feat(templating): great progress on adhoc filters, #6038 --- public/app/core/utils/kbn.js | 4 + .../app/features/dashboard/ad_hoc_filters.ts | 159 ++++++++++++++++++ public/app/features/dashboard/all.js | 1 + .../features/dashboard/submenu/submenu.html | 12 +- public/app/features/templating/editorCtrl.js | 2 +- .../features/templating/partials/editor.html | 13 +- .../plugins/datasource/influxdb/datasource.ts | 50 ++++-- .../datasource/influxdb/influx_query.ts | 19 ++- .../datasource/prometheus/datasource.ts | 9 +- public/sass/components/_gf-form.scss | 1 - public/sass/components/_submenu.scss | 15 +- 11 files changed, 239 insertions(+), 46 deletions(-) create mode 100644 public/app/features/dashboard/ad_hoc_filters.ts diff --git a/public/app/core/utils/kbn.js b/public/app/core/utils/kbn.js index c0d162b0ece..00c0cb3e127 100644 --- a/public/app/core/utils/kbn.js +++ b/public/app/core/utils/kbn.js @@ -9,6 +9,10 @@ function($, _, moment) { var kbn = {}; kbn.valueFormats = {}; + kbn.regexEscape = function(value) { + return value.replace(/[\\^$*+?.()|[\]{}]/g, '\\\\$&'); + }; + ///// HELPER FUNCTIONS ///// kbn.round_interval = function(interval) { diff --git a/public/app/features/dashboard/ad_hoc_filters.ts b/public/app/features/dashboard/ad_hoc_filters.ts new file mode 100644 index 00000000000..c97c5588666 --- /dev/null +++ b/public/app/features/dashboard/ad_hoc_filters.ts @@ -0,0 +1,159 @@ +/// + +import _ from 'lodash'; +import angular from 'angular'; +import coreModule from 'app/core/core_module'; + +export class AdHocFiltersCtrl { + segments: any; + variable: any; + removeTagFilterSegment: any; + + /** @ngInject */ + constructor(private uiSegmentSrv, private datasourceSrv, private $q, private templateSrv, private $rootScope) { + this.removeTagFilterSegment = uiSegmentSrv.newSegment({fake: true, value: '-- remove filter --'}); + this.buildSegmentModel(); + } + + buildSegmentModel() { + this.segments = []; + + if (this.variable.value && !_.isArray(this.variable.value)) { + } + + for (let tag of this.variable.value) { + if (this.segments.length > 0) { + this.segments.push(this.uiSegmentSrv.newCondition('AND')); + } + + if (tag.key !== undefined && tag.value !== undefined) { + this.segments.push(this.uiSegmentSrv.newKey(tag.key)); + this.segments.push(this.uiSegmentSrv.newOperator(tag.operator)); + this.segments.push(this.uiSegmentSrv.newKeyValue(tag.value)); + } + } + + this.segments.push(this.uiSegmentSrv.newPlusButton()); + } + + getOptions(segment, index) { + if (segment.type === 'operator') { + return this.$q.when(this.uiSegmentSrv.newOperators(['=', '!=', '<>', '<', '>', '=~', '!~'])); + } + + return this.datasourceSrv.get(this.variable.datasource).then(ds => { + var options: any = {}; + var promise = null; + + if (segment.type !== 'value') { + promise = ds.getTagKeys(); + } else { + options.key = this.segments[index-2].value; + promise = ds.getTagValues(options); + } + + return promise.then(results => { + results = _.map(results, segment => { + return this.uiSegmentSrv.newSegment({value: segment.text}); + }); + + // add remove option for keys + if (segment.type === 'key') { + results.splice(0, 0, angular.copy(this.removeTagFilterSegment)); + } + return results; + }); + }); + } + + segmentChanged(segment, index) { + this.segments[index] = segment; + + // handle remove tag condition + if (segment.value === this.removeTagFilterSegment.value) { + this.segments.splice(index, 3); + if (this.segments.length === 0) { + this.segments.push(this.uiSegmentSrv.newPlusButton()); + } else if (this.segments.length > 2) { + this.segments.splice(Math.max(index-1, 0), 1); + if (this.segments[this.segments.length-1].type !== 'plus-button') { + this.segments.push(this.uiSegmentSrv.newPlusButton()); + } + } + } else { + if (segment.type === 'plus-button') { + if (index > 2) { + this.segments.splice(index, 0, this.uiSegmentSrv.newCondition('AND')); + } + this.segments.push(this.uiSegmentSrv.newOperator('=')); + this.segments.push(this.uiSegmentSrv.newFake('select tag value', 'value', 'query-segment-value')); + segment.type = 'key'; + segment.cssClass = 'query-segment-key'; + } + + if ((index+1) === this.segments.length) { + this.segments.push(this.uiSegmentSrv.newPlusButton()); + } + } + + this.updateVariableModel(); + } + + updateVariableModel() { + var tags = []; + var tagIndex = -1; + var tagOperator = ""; + + this.segments.forEach((segment, index) => { + if (segment.fake) { + return; + } + + switch (segment.type) { + case 'key': { + tags.push({key: segment.value}); + tagIndex += 1; + break; + } + case 'value': { + tags[tagIndex].value = segment.value; + break; + } + case 'operator': { + tags[tagIndex].operator = segment.value; + break; + } + case 'condition': { + break; + } + } + }); + + this.$rootScope.$broadcast('refresh'); + this.variable.value = tags; + } +} + +var template = ` +
+
+ +
+
+`; + +export function adHocFiltersComponent() { + return { + restrict: 'E', + template: template, + controller: AdHocFiltersCtrl, + bindToController: true, + controllerAs: 'ctrl', + scope: { + variable: "=" + } + }; +} + +coreModule.directive('adHocFilters', adHocFiltersComponent); diff --git a/public/app/features/dashboard/all.js b/public/app/features/dashboard/all.js index 6aea2efa9f1..5ff221af85a 100644 --- a/public/app/features/dashboard/all.js +++ b/public/app/features/dashboard/all.js @@ -20,4 +20,5 @@ define([ './import/dash_import', './export/export_modal', './dash_list_ctrl', + './ad_hoc_filters', ], function () {}); diff --git a/public/app/features/dashboard/submenu/submenu.html b/public/app/features/dashboard/submenu/submenu.html index f02b3cfc881..1e1513d4021 100644 --- a/public/app/features/dashboard/submenu/submenu.html +++ b/public/app/features/dashboard/submenu/submenu.html @@ -1,19 +1,13 @@