Merge branch 'elastic_annotations'

Conflicts:
	src/css/bootstrap.dark.min.css
	src/css/bootstrap.light.min.css
	src/css/default.min.css
This commit is contained in:
Torkel Ödegaard 2014-07-30 11:36:47 +02:00
commit d2a342a94e
23 changed files with 350 additions and 255 deletions

View File

@ -12,6 +12,7 @@
- [Issue #610](https://github.com/grafana/grafana/issues/610). InfluxDB: Support for InfluxdB v0.8 list series response schemea (series typeahead)
- [Issue #266](https://github.com/grafana/grafana/issues/266). Graphite: New option cacheTimeout to override graphite default memcache timeout
- [Issue #606](https://github.com/grafana/grafana/issues/606). General: New global option in config.js to specify admin password (useful to hinder users from accidentally make changes)
- [Issue #201](https://github.com/grafana/grafana/issues/201). Annotations: Elasticsearch datasource support for events
**Changes**
- [Issue #536](https://github.com/grafana/grafana/issues/536). Graphite: Use unix epoch for Graphite from/to for absolute time ranges

View File

@ -13,18 +13,10 @@ function (_, crypto) {
* @type {Object}
*/
var defaults = {
elasticsearch : "http://"+window.location.hostname+":9200",
datasources : {
default: {
url: "http://"+window.location.hostname+":8080",
default: true
}
},
datasources : {},
panels : ['graph', 'text'],
plugins : {},
default_route : '/dashboard/file/default.json',
grafana_index : 'grafana-dash',
elasticsearch_all_disabled : false,
playlist_timespan : "1m",
unsaved_changes_warning : true,
admin: {}
@ -57,13 +49,21 @@ function (_, crypto) {
return datasource;
};
// backward compatible with old config
if (options.graphiteUrl) {
settings.datasources = {
graphite: {
type: 'graphite',
url: options.graphiteUrl,
default: true
}
settings.datasources.graphite = {
type: 'graphite',
url: options.graphiteUrl,
default: true
};
}
if (options.elasticsearch) {
settings.datasources.elasticsearch = {
type: 'elasticsearch',
url: options.elasticsearch,
index: options.grafana_index,
grafanaDB: true
};
}
@ -73,10 +73,6 @@ function (_, crypto) {
if (datasource.type === 'influxdb') { parseMultipleHosts(datasource); }
});
var elasticParsed = parseBasicAuth({ url: settings.elasticsearch });
settings.elasticsearchBasicAuth = elasticParsed.basicAuth;
settings.elasticsearch = elasticParsed.url;
if (settings.plugins.panels) {
settings.panels = _.union(settings.panels, settings.plugins.panels);
}

View File

@ -2,16 +2,18 @@ define([
'angular',
'underscore',
'moment',
'config',
'filesaver'
],
function (angular, _, moment) {
function (angular, _, moment, config) {
'use strict';
var module = angular.module('grafana.controllers');
module.controller('dashLoader', function($scope, $rootScope, $http, alertSrv, $location, playlistSrv, elastic) {
module.controller('dashLoader', function($scope, $rootScope, $http, alertSrv, $location, playlistSrv, datasourceSrv) {
$scope.init = function() {
$scope.db = datasourceSrv.getGrafanaDB();
$scope.onAppEvent('save-dashboard', function() {
$scope.saveDashboard();
});
@ -19,7 +21,6 @@ function (angular, _, moment) {
$scope.onAppEvent('zoom-out', function() {
$scope.zoom(2);
});
};
$scope.exitFullscreen = function() {
@ -38,6 +39,9 @@ function (angular, _, moment) {
if(type === 'save') {
return (_l.save_elasticsearch);
}
if(type === 'share') {
return (_l.save_temp);
}
return false;
};
@ -52,7 +56,7 @@ function (angular, _, moment) {
};
$scope.saveForSharing = function() {
elastic.saveForSharing($scope.dashboard)
$scope.db.saveDashboardTemp($scope.dashboard)
.then(function(result) {
$scope.share = { url: result.url, title: result.title };
@ -62,10 +66,32 @@ function (angular, _, moment) {
});
};
$scope.passwordCache = function(pwd) {
if (!window.sessionStorage) { return null; }
if (!pwd) { return window.sessionStorage["grafanaAdminPassword"]; }
window.sessionStorage["grafanaAdminPassword"] = pwd;
};
$scope.isAdmin = function() {
if (!config.admin || !config.admin.password) { return true; }
if (this.passwordCache() === config.admin.password) { return true; }
var password = window.prompt("Admin password", "");
this.passwordCache(password);
if (password === config.admin.password) { return true; }
alertSrv.set('Save failed', 'Password incorrect', 'error');
return false;
};
$scope.saveDashboard = function() {
elastic.saveDashboard($scope.dashboard, $scope.dashboard.title)
if (!this.isAdmin()) { return false; }
$scope.db.saveDashboard($scope.dashboard, $scope.dashboard.title)
.then(function(result) {
alertSrv.set('Dashboard Saved', 'Dashboard has been saved to Elasticsearch as "' + result.title + '"','success', 5000);
alertSrv.set('Dashboard Saved', 'Dashboard has been saved as "' + result.title + '"','success', 5000);
$location.path(result.url);
@ -81,7 +107,9 @@ function (angular, _, moment) {
return;
}
elastic.deleteDashboard(id).then(function(id) {
if (!this.isAdmin()) { return false; }
$scope.db.deleteDashboard(id).then(function(id) {
alertSrv.set('Dashboard Deleted', id + ' has been deleted', 'success', 5000);
}, function() {
alertSrv.set('Dashboard Not Deleted', 'An error occurred deleting the dashboard', 'error', 5000);

View File

@ -11,8 +11,7 @@ function (angular, app, _) {
module.controller('GraphiteImportCtrl', function($scope, $rootScope, $timeout, datasourceSrv) {
$scope.init = function() {
console.log('hej!');
$scope.datasources = datasourceSrv.listOptions();
$scope.datasources = datasourceSrv.getMetricSources();
$scope.setDatasource(null);
};
@ -96,7 +95,8 @@ function (angular, app, _) {
currentRow.panels.push(panel);
});
$scope.dashboard.dash_load(newDashboard);
$scope.emitAppEvent('setup-dashboard', newDashboard);
$scope.dismiss();
}
});

View File

@ -9,13 +9,14 @@ function (angular, _, config, $) {
var module = angular.module('grafana.controllers');
module.controller('SearchCtrl', function($scope, $rootScope, $element, $location, elastic) {
module.controller('SearchCtrl', function($scope, $rootScope, $element, $location, datasourceSrv) {
$scope.init = function() {
$scope.giveSearchFocus = 0;
$scope.selectedIndex = -1;
$scope.results = {dashboards: [], tags: [], metrics: []};
$scope.query = { query: 'title:' };
$scope.db = datasourceSrv.getGrafanaDB();
$scope.onAppEvent('open-search', $scope.openSearch);
};
@ -57,40 +58,12 @@ function (angular, _, config, $) {
};
};
$scope.searchDasboards = function(queryString) {
var tagsOnly = queryString.indexOf('tags!:') === 0;
if (tagsOnly) {
var tagsQuery = queryString.substring(6, queryString.length);
queryString = 'tags:' + tagsQuery + '*';
}
else {
if (queryString.length === 0) {
queryString = 'title:';
}
if (queryString[queryString.length - 1] !== '*') {
queryString += '*';
}
}
var query = {
query: { query_string: { query: queryString } },
facets: { tags: { terms: { field: "tags", order: "term", size: 50 } } },
size: 20,
sort: ["_uid"]
};
return elastic.post('/dashboard/_search', query)
$scope.searchDashboards = function(queryString) {
return $scope.db.searchDashboards(queryString)
.then(function(results) {
if(_.isUndefined(results.hits)) {
$scope.results.dashboards = [];
$scope.results.tags = [];
return;
}
$scope.tagsOnly = tagsOnly;
$scope.results.dashboards = results.hits.hits;
$scope.results.tags = results.facets.tags.terms;
$scope.tagsOnly = results.dashboards.length === 0 && results.tags.length > 0;
$scope.results.dashboards = results.dashboards;
$scope.results.tags = results.tags;
});
};
@ -118,12 +91,9 @@ function (angular, _, config, $) {
$scope.selectedIndex = -1;
var queryStr = $scope.query.query.toLowerCase();
queryStr = queryStr.replace(' and ', ' AND ');
if (queryStr.indexOf('m:') !== 0) {
queryStr = queryStr.replace(' and ', ' AND ');
$scope.searchDasboards(queryStr);
return;
}
$scope.searchDashboards(queryStr);
};
$scope.openSearch = function (evt) {
@ -177,4 +147,4 @@ function (angular, _, config, $) {
};
});
});
});

View File

@ -12,7 +12,7 @@ function (angular, app, _) {
var module = angular.module('grafana.panels.annotations', []);
app.useModule(module);
module.controller('AnnotationsEditorCtrl', function($scope, datasourceSrv, $rootScope) {
module.controller('AnnotationsEditorCtrl', function($scope, datasourceSrv) {
var annotationDefaults = {
name: '',

View File

@ -220,7 +220,7 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
$scope.editorTabs = _.pluck($scope.panelMeta.fullEditorTabs,'title');
$scope.hiddenSeries = {};
$scope.datasources = datasourceSrv.listOptions();
$scope.datasources = datasourceSrv.getMetricSources();
$scope.setDatasource($scope.panel.datasource);
if ($scope.panel.targets.length === 0) {

View File

@ -27,7 +27,7 @@
<li ng-show="dashboard.loader.save_elasticsearch">
<form class="input-prepend nomargin save-dashboard-dropdown-save-form">
<input class='input-medium' ng-model="dashboard.title" type="text" ng-model="elasticsearch.title"/>
<input class='input-medium' ng-model="dashboard.title" type="text" />
<button class="btn" ng-click="saveDashboard()"><i class="icon-save"></i></button>
</form>
</li>
@ -47,8 +47,11 @@
<li ng-show="dashboard.loader.save_local">
<a class="link" ng-click="exportDashboard()">Export dashboard</a>
</li>
<li ng-show="showDropdown('share')"><a bs-tooltip="'Share'" data-placement="bottom" ng-click="saveForSharing()" config-modal="app/partials/dashLoaderShare.html">Share temp copy</i></a></li>
<li ng-show="showDropdown('share')">
<a bs-tooltip="'Share'" data-placement="bottom" ng-click="saveForSharing()" config-modal="app/partials/dashLoaderShare.html">
Share temp copy
</a>
</li>
</ul>
</li>

View File

@ -2,7 +2,7 @@
<div class="pull-right editor-title">Dashboard settings</div>
<div ng-model="editor.index" bs-tabs style="text-transform:capitalize;">
<div ng-repeat="tab in ['General', 'Rows','Controls', 'Metrics', 'Import']" data-title="{{tab}}">
<div ng-repeat="tab in ['General', 'Rows','Controls', 'Import']" data-title="{{tab}}">
</div>
<div ng-repeat="tab in dashboard.nav" data-title="{{tab.type}}">
</div>
@ -111,11 +111,7 @@
</div>
</div>
<div ng-if="editor.index == 3">
<ng-include src="'app/partials/loadmetrics.html'"></ng-include>
</div>
<div ng-if="editor.index == 4">
<div ng-if="editor.index == 3">
<ng-include src="'app/partials/import.html'"></ng-include>
</div>

View File

@ -1 +1,39 @@
<h2>Elasticsearch</h2>
<div class="editor-row">
<div class="section">
<h5>Index name</h5>
<div class="editor-option">
<input type="text" class="span4" ng-model='currentAnnotation.index' placeholder="events-*"></input>
</div>
</div>
<div class="section">
<h5>Search query (lucene) <tip>Use [[filterName]] in query to replace part of the query with a filter value</h5>
<div class="editor-option">
<input type="text" class="span6" ng-model='currentAnnotation.query' placeholder="tags:deploy"></input>
</div>
</div>
</div>
<div class="editor-row">
<div class="section">
<h5>Field mappings</h5>
<div class="editor-option">
<label class="small">Time</label>
<input type="text" class="input-small" ng-model='currentAnnotation.timeField' placeholder="@timestamp"></input>
</div>
<div class="editor-option">
<label class="small">Title</label>
<input type="text" class="input-small" ng-model='currentAnnotation.titleField' placeholder="desc"></input>
</div>
<div class="editor-option">
<label class="small">Tags</label>
<input type="text" class="input-small" ng-model='currentAnnotation.tagsField' placeholder="tags"></input>
</div>
<div class="editor-option">
<label class="small">Text</label>
<input type="text" class="input-small" ng-model='currentAnnotation.textField' placeholder=""></input>
</div>
</div>
</div>

View File

@ -1,9 +1,7 @@
define([
'angular',
'jquery',
'config'
],
function (angular, $, config) {
function (angular) {
"use strict";
var module = angular.module('grafana.routes');
@ -20,37 +18,15 @@ function (angular, $, config) {
});
});
module.controller('DashFromElasticProvider', function($scope, $rootScope, elastic, $routeParams, alertSrv) {
module.controller('DashFromElasticProvider', function($scope, $rootScope, datasourceSrv, $routeParams, alertSrv) {
var elasticsearch_load = function(id) {
var url = '/dashboard/' + id;
// hack to check if it is a temp dashboard
if (window.location.href.indexOf('dashboard/temp') > 0) {
url = '/temp/' + id;
}
return elastic.get(url)
.then(function(result) {
if (result._source && result._source.dashboard) {
return angular.fromJson(result._source.dashboard);
} else {
return false;
}
}, function(data, status) {
if(status === 0) {
alertSrv.set('Error',"Could not contact Elasticsearch at " +
config.elasticsearch + ". Please ensure that Elasticsearch is reachable from your browser.",'error');
} else {
alertSrv.set('Error',"Could not find dashboard " + id, 'error');
}
return false;
});
};
elasticsearch_load($routeParams.id).then(function(dashboard) {
$scope.emitAppEvent('setup-dashboard', dashboard);
});
var db = datasourceSrv.getGrafanaDB();
db.getDashboard($routeParams.id)
.then(function(dashboard) {
$scope.emitAppEvent('setup-dashboard', dashboard);
}).then(null, function(error) {
alertSrv.set('Error', error, 'error');
});
});

View File

@ -8,8 +8,7 @@ define([
'./annotationsSrv',
'./playlistSrv',
'./unsavedChangesSrv',
'./elasticsearch/es-client',
'./dashboard/dashboardKeyBindings',
'./dashboard/dashboardModel',
],
function () {});
function () {});

View File

@ -37,6 +37,7 @@ define([
var promises = _.map(annotations, function(annotation) {
var datasource = datasourceSrv.get(annotation.datasource);
return datasource.annotationQuery(annotation, filterSrv, rangeUnparsed)
.then(this.receiveAnnotationResults)
.then(null, errorHandler);
@ -64,7 +65,7 @@ define([
function addAnnotation(options) {
var tooltip = "<small><b>" + options.title + "</b><br/>";
if (options.tags) {
tooltip += (options.tags || '') + '<br/>';
tooltip += '<span class="tag label label-tag">' + (options.tags || '') + '</span><br/>';
}
if (timezone === 'browser') {

View File

@ -14,6 +14,9 @@ function (angular, _, config) {
module.service('datasourceSrv', function($q, filterSrv, $http, $injector) {
var datasources = {};
var metricSources = [];
var annotationSources = [];
var grafanaDB = {};
this.init = function() {
_.each(config.datasources, function(value, key) {
@ -27,6 +30,26 @@ function (angular, _, config) {
this.default = datasources[_.keys(datasources)[0]];
this.default.default = true;
}
// create list of different source types
_.each(datasources, function(value, key) {
if (value.supportMetrics) {
metricSources.push({
name: value.name,
value: value.default ? null : key,
});
}
if (value.supportAnnotations) {
annotationSources.push({
name: key,
editorSrc: value.annotationEditorSrc,
});
}
if (value.grafanaDB) {
grafanaDB = value;
}
});
};
this.datasourceFactory = function(ds) {
@ -56,25 +79,15 @@ function (angular, _, config) {
};
this.getAnnotationSources = function() {
var results = [];
_.each(datasources, function(value, key) {
if (value.supportAnnotations) {
results.push({
name: key,
editorSrc: value.annotationEditorSrc,
});
}
});
return results;
return annotationSources;
};
this.listOptions = function() {
return _.map(config.datasources, function(value, key) {
return {
name: value.default ? key + ' (default)' : key,
value: value.default ? null : key
};
});
this.getMetricSources = function() {
return metricSources;
};
this.getGrafanaDB = function() {
return grafanaDB;
};
this.init();

View File

@ -1,116 +0,0 @@
define([
'angular',
'config'
],
function(angular, config) {
"use strict";
var module = angular.module('grafana.services');
module.service('elastic', function($http, $q) {
this._request = function(method, url, data) {
var options = {
url: config.elasticsearch + "/" + config.grafana_index + url,
method: method,
data: data
};
if (config.elasticsearchBasicAuth) {
options.headers = {
"Authorization": "Basic " + config.elasticsearchBasicAuth
};
}
return $http(options);
};
this.get = function(url) {
return this._request('GET', url)
.then(function(results) {
return results.data;
});
};
this.post = function(url, data) {
return this._request('POST', url, data)
.then(function(results) {
return results.data;
});
};
this.deleteDashboard = function(id) {
if (!this.isAdmin()) { return $q.reject("Invalid admin password"); }
return this._request('DELETE', '/dashboard/' + id)
.then(function(result) {
return result.data._id;
}, function(err) {
throw err.data;
});
};
this.saveForSharing = function(dashboard) {
var data = {
user: 'guest',
group: 'guest',
title: dashboard.title,
tags: dashboard.tags,
dashboard: angular.toJson(dashboard)
};
var ttl = dashboard.loader.save_temp_ttl;
return this._request('POST', '/temp/?ttl=' + ttl, data)
.then(function(result) {
var baseUrl = window.location.href.replace(window.location.hash,'');
var url = baseUrl + "#dashboard/temp/" + result.data._id;
return { title: dashboard.title, url: url };
}, function(err) {
throw "Failed to save to temp dashboard to elasticsearch " + err.data;
});
};
this.passwordCache = function(pwd) {
if (!window.sessionStorage) { return null; }
if (!pwd) { return window.sessionStorage["grafanaAdminPassword"]; }
window.sessionStorage["grafanaAdminPassword"] = pwd;
};
this.isAdmin = function() {
if (!config.admin || !config.admin.password) { return true; }
if (this.passwordCache() === config.admin.password) { return true; }
var password = window.prompt("Admin password", "");
this.passwordCache(password);
return password === config.admin.password;
};
this.saveDashboard = function(dashboard, title) {
if (!this.isAdmin()) { return $q.reject("Invalid admin password"); }
var dashboardClone = angular.copy(dashboard);
title = dashboardClone.title = title ? title : dashboard.title;
var data = {
user: 'guest',
group: 'guest',
title: title,
tags: dashboardClone.tags,
dashboard: angular.toJson(dashboardClone)
};
return this._request('PUT', '/dashboard/' + encodeURIComponent(title), data)
.then(function() {
return { title: title, url: '/dashboard/elasticsearch/' + title };
}, function(err) {
throw 'Failed to save to elasticsearch ' + err.data;
});
};
});
});

View File

@ -19,10 +19,197 @@ function (angular, _, $, config, kbn, moment) {
this.url = datasource.url;
this.name = datasource.name;
this.supportAnnotations = true;
this.supportMetrics = false;
this.index = datasource.index;
this.grafanaDB = datasource.grafanaDB;
this.annotationEditorSrc = 'app/partials/elasticsearch/annotation_editor.html';
}
ElasticDatasource.prototype._request = function(method, url, index, data) {
var options = {
url: this.url + "/" + index + url,
method: method,
data: data
};
if (this.basicAuth) {
options.headers = {
"Authorization": "Basic " + this.basicAuth
};
}
return $http(options);
};
ElasticDatasource.prototype._get = function(url) {
return this._request('GET', url, this.index)
.then(function(results) {
return results.data;
});
};
ElasticDatasource.prototype._post = function(url, data) {
return this._request('POST', url, this.index, data)
.then(function(results) {
return results.data;
});
};
ElasticDatasource.prototype.annotationQuery = function(annotation, filterSrv, rangeUnparsed) {
var range = {};
var timeField = annotation.timeField || '@timestamp';
var queryString = annotation.query || '*';
var tagsField = annotation.tagsField || 'tags';
var titleField = annotation.titleField || 'desc';
var textField = annotation.textField || null;
range[annotation.timeField]= {
from: rangeUnparsed.from,
to: rangeUnparsed.to,
};
var filter = { "bool": { "must": [{ "range": range }] } };
var query = { "bool": { "should": [{ "query_string": { "query": queryString } }] } };
var data = { "query" : { "filtered": { "query" : query, "filter": filter } }, "size": 100 };
return this._request('POST', '/_search', annotation.index, data).then(function(results) {
var list = [];
var hits = results.data.hits.hits;
for (var i = 0; i < hits.length; i++) {
var source = hits[i]._source;
var event = {
annotation: annotation,
time: moment.utc(source[timeField]).valueOf(),
title: source[titleField],
};
if (source[tagsField]) {
if (_.isArray(source[tagsField])) {
event.tags = source[tagsField].join(', ');
}
else {
event.tags = source[tagsField];
}
}
if (textField && source[textField]) {
event.text = source[textField];
}
list.push(event);
}
return list;
});
};
ElasticDatasource.prototype.getDashboard = function(id) {
var url = '/dashboard/' + id;
// hack to check if it is a temp dashboard
if (window.location.href.indexOf('dashboard/temp') > 0) {
url = '/temp/' + id;
}
return this._get(url)
.then(function(result) {
if (result._source && result._source.dashboard) {
return angular.fromJson(result._source.dashboard);
} else {
return false;
}
}, function(data) {
if(data.status === 0) {
throw "Could not contact Elasticsearch. Please ensure that Elasticsearch is reachable from your browser.";
} else {
throw "Could not find dashboard " + id;
}
});
};
ElasticDatasource.prototype.saveDashboard = function(dashboard, title) {
var dashboardClone = angular.copy(dashboard);
title = dashboardClone.title = title ? title : dashboard.title;
var data = {
user: 'guest',
group: 'guest',
title: title,
tags: dashboardClone.tags,
dashboard: angular.toJson(dashboardClone)
};
return this._request('PUT', '/dashboard/' + encodeURIComponent(title), this.index, data)
.then(function() {
return { title: title, url: '/dashboard/elasticsearch/' + title };
}, function(err) {
throw 'Failed to save to elasticsearch ' + err.data;
});
};
ElasticDatasource.prototype.saveDashboardTemp = function(dashboard) {
var data = {
user: 'guest',
group: 'guest',
title: dashboard.title,
tags: dashboard.tags,
dashboard: angular.toJson(dashboard)
};
var ttl = dashboard.loader.save_temp_ttl;
return this._request('POST', '/temp/?ttl=' + ttl, this.index, data)
.then(function(result) {
var baseUrl = window.location.href.replace(window.location.hash,'');
var url = baseUrl + "#dashboard/temp/" + result.data._id;
return { title: dashboard.title, url: url };
}, function(err) {
throw "Failed to save to temp dashboard to elasticsearch " + err.data;
});
};
ElasticDatasource.prototype.deleteDashboard = function(id) {
return this._request('DELETE', '/dashboard/' + id, this.index)
.then(function(result) {
return result.data._id;
}, function(err) {
throw err.data;
});
};
ElasticDatasource.prototype.searchDashboards = function(queryString) {
var tagsOnly = queryString.indexOf('tags!:') === 0;
if (tagsOnly) {
var tagsQuery = queryString.substring(6, queryString.length);
queryString = 'tags:' + tagsQuery + '*';
}
else {
if (queryString.length === 0) {
queryString = 'title:';
}
if (queryString[queryString.length - 1] !== '*') {
queryString += '*';
}
}
var query = {
query: { query_string: { query: queryString } },
facets: { tags: { terms: { field: "tags", order: "term", size: 50 } } },
size: 20,
sort: ["_uid"]
};
return this._post('/dashboard/_search', query)
.then(function(results) {
if(_.isUndefined(results.hits)) {
return { dashboards: [], tags: [] };
}
return { dashboards: results.hits.hits, tags: results.facets.terms };
});
};
return ElasticDatasource;

View File

@ -21,6 +21,7 @@ function (angular, _, $, config, kbn, moment) {
this.name = datasource.name;
this.render_method = datasource.render_method || 'POST';
this.supportAnnotations = true;
this.supportMetrics = true;
this.annotationEditorSrc = 'app/partials/graphite/annotation_editor.html';
this.cacheTimeout = datasource.cacheTimeout;
}

View File

@ -23,6 +23,7 @@ function (angular, _, kbn, InfluxSeries) {
};
this.supportAnnotations = true;
this.supportMetrics = true;
this.annotationEditorSrc = 'app/partials/influxdb/annotation_editor.html';
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -128,4 +128,6 @@
}
}
.annotation-tags {
color: @purple;
}

View File

@ -12,7 +12,6 @@ module.exports = function(config) {
'app/routes/**/*.js',
'app/app.js',
'vendor/angular/**/*.js',
'vendor/elasticjs/elastic-angular-client.js'
],
dest: '<%= tempDir %>'
}