From cdba2bd184c6060abb64bf69d9f00acdfef25930 Mon Sep 17 00:00:00 2001 From: David Kaltschmidt Date: Wed, 6 Jun 2018 16:11:40 +0200 Subject: [PATCH] Annotations support for ifql datasource --- .../datasource/influxdb-ifql/README.md | 1 - .../datasource/influxdb-ifql/datasource.ts | 27 +++++++++-- .../partials/annotations.editor.html | 14 +++--- .../datasource/influxdb-ifql/plugin.json | 2 +- .../influxdb-ifql/response_parser.ts | 46 +++++++++++++++++-- .../specs/response_parser.jest.ts | 12 +++++ 6 files changed, 82 insertions(+), 20 deletions(-) diff --git a/public/app/plugins/datasource/influxdb-ifql/README.md b/public/app/plugins/datasource/influxdb-ifql/README.md index ce3917c1be2..5acb563b453 100644 --- a/public/app/plugins/datasource/influxdb-ifql/README.md +++ b/public/app/plugins/datasource/influxdb-ifql/README.md @@ -25,6 +25,5 @@ Read more about InfluxDB here: - Syntax highlighting - Tab completion (functions, values) -- Annotations support - Alerting integration - Explore UI integration diff --git a/public/app/plugins/datasource/influxdb-ifql/datasource.ts b/public/app/plugins/datasource/influxdb-ifql/datasource.ts index ab9886baaca..c71b48ad96b 100644 --- a/public/app/plugins/datasource/influxdb-ifql/datasource.ts +++ b/public/app/plugins/datasource/influxdb-ifql/datasource.ts @@ -2,7 +2,13 @@ import _ from 'lodash'; import * as dateMath from 'app/core/utils/datemath'; -import { getTableModelFromResult, getTimeSeriesFromResult, getValuesFromResult, parseResults } from './response_parser'; +import { + getAnnotationsFromResult, + getTableModelFromResult, + getTimeSeriesFromResult, + getValuesFromResult, + parseResults, +} from './response_parser'; import expandMacros from './metric_find_query'; function serializeParams(params) { @@ -101,11 +107,22 @@ export default class InfluxDatasource { }); } - var timeFilter = this.getTimeFilter({ rangeRaw: options.rangeRaw }); - var query = options.annotation.query.replace('$timeFilter', timeFilter); - query = this.templateSrv.replace(query, null, 'regex'); + const { query } = options.annotation; + const queryOptions = { + scopedVars: {}, + ...options, + silent: true, + }; + const target = this.prepareQueryTarget({ query }, queryOptions); - return {}; + return this._seriesQuery(target.query, queryOptions).then(response => { + const results = parseResults(response.data); + if (results.length === 0) { + throw { message: 'No results in response from InfluxDB' }; + } + const annotations = _.flatten(results.map(result => getAnnotationsFromResult(result, options.annotation))); + return annotations; + }); } metricFindQuery(query: string, options?: any) { diff --git a/public/app/plugins/datasource/influxdb-ifql/partials/annotations.editor.html b/public/app/plugins/datasource/influxdb-ifql/partials/annotations.editor.html index 48991426c1e..53407f2a3f2 100644 --- a/public/app/plugins/datasource/influxdb-ifql/partials/annotations.editor.html +++ b/public/app/plugins/datasource/influxdb-ifql/partials/annotations.editor.html @@ -1,11 +1,13 @@ -
- +
-
Field mappings If your influxdb query returns more than one field you need to specify the column names below. An annotation event is composed of a title, tags, and an additional text field.
+
Field mappings + If your influxdb query returns more than one field you need to specify the column names below. An annotation event is composed + of a title, tags, and an additional text field. +
@@ -16,9 +18,5 @@ Tags
-
- Title (deprecated) - -
-
+ \ No newline at end of file diff --git a/public/app/plugins/datasource/influxdb-ifql/plugin.json b/public/app/plugins/datasource/influxdb-ifql/plugin.json index b4eb764d556..f87e1c33275 100644 --- a/public/app/plugins/datasource/influxdb-ifql/plugin.json +++ b/public/app/plugins/datasource/influxdb-ifql/plugin.json @@ -4,7 +4,7 @@ "id": "influxdb-ifql", "defaultMatchFormat": "regex values", "metrics": true, - "annotations": false, + "annotations": true, "alerting": false, "queryOptions": { "minInterval": true diff --git a/public/app/plugins/datasource/influxdb-ifql/response_parser.ts b/public/app/plugins/datasource/influxdb-ifql/response_parser.ts index 5481b197726..dace2cf63d2 100644 --- a/public/app/plugins/datasource/influxdb-ifql/response_parser.ts +++ b/public/app/plugins/datasource/influxdb-ifql/response_parser.ts @@ -1,4 +1,5 @@ import Papa from 'papaparse'; +import flatten from 'lodash/flatten'; import groupBy from 'lodash/groupBy'; import TableModel from 'app/core/table_model'; @@ -6,17 +7,25 @@ import TableModel from 'app/core/table_model'; const filterColumnKeys = key => key && key[0] !== '_' && key !== 'result' && key !== 'table'; const IGNORE_FIELDS_FOR_NAME = ['result', '', 'table']; + +export const getTagsFromRecord = record => + Object.keys(record) + .filter(key => key[0] !== '_') + .filter(key => IGNORE_FIELDS_FOR_NAME.indexOf(key) === -1) + .reduce((tags, key) => { + tags[key] = record[key]; + return tags; + }, {}); + export const getNameFromRecord = record => { // Measurement and field const metric = [record._measurement, record._field]; // Add tags - const tags = Object.keys(record) - .filter(key => key[0] !== '_') - .filter(key => IGNORE_FIELDS_FOR_NAME.indexOf(key) === -1) - .map(key => `${key}=${record[key]}`); + const tags = getTagsFromRecord(record); + const tagValues = Object.keys(tags).map(key => `${key}=${tags[key]}`); - return [...metric, ...tags].join(' '); + return [...metric, ...tagValues].join(' '); }; const parseCSV = (input: string) => @@ -36,6 +45,33 @@ export function parseResults(response: string): any[] { return response.trim().split(/\n\s*\s/); } +export function getAnnotationsFromResult(result: string, options: any) { + const data = parseCSV(result); + if (data.length === 0) { + return []; + } + + const annotations = []; + const textSelector = options.textCol || '_value'; + const tagsSelector = options.tagsCol || ''; + const tagSelection = tagsSelector.split(',').map(t => t.trim()); + + data.forEach(record => { + // Remove empty values, then split in different tags for comma separated values + const tags = getTagsFromRecord(record); + const tagValues = flatten(tagSelection.filter(tag => tags[tag]).map(tag => tags[tag].split(','))); + + annotations.push({ + annotation: options, + time: parseTime(record._time), + tags: tagValues, + text: record[textSelector], + }); + }); + + return annotations; +} + export function getTableModelFromResult(result: string) { const data = parseCSV(result); diff --git a/public/app/plugins/datasource/influxdb-ifql/specs/response_parser.jest.ts b/public/app/plugins/datasource/influxdb-ifql/specs/response_parser.jest.ts index cfd88729fa0..9a322499463 100644 --- a/public/app/plugins/datasource/influxdb-ifql/specs/response_parser.jest.ts +++ b/public/app/plugins/datasource/influxdb-ifql/specs/response_parser.jest.ts @@ -1,4 +1,5 @@ import { + getAnnotationsFromResult, getNameFromRecord, getTableModelFromResult, getTimeSeriesFromResult, @@ -16,6 +17,17 @@ describe('influxdb ifql response parser', () => { }); }); + describe('getAnnotationsFromResult()', () => { + it('expects a list of annotations', () => { + const results = parseResults(response); + const annotations = getAnnotationsFromResult(results[0], { tagsCol: 'cpu' }); + expect(annotations.length).toBe(300); + expect(annotations[0].tags.length).toBe(1); + expect(annotations[0].tags[0]).toBe('cpu-total'); + expect(annotations[0].text).toBe('0'); + }); + }); + describe('getTableModelFromResult()', () => { it('expects a table model', () => { const results = parseResults(response);