grafana/src/app/services/dashboard.js

417 lines
12 KiB
JavaScript
Raw Normal View History

2013-09-13 15:52:13 -05:00
define([
'angular',
'jquery',
'kbn',
2013-09-13 15:52:13 -05:00
'underscore',
'config',
'moment',
'modernizr',
'filesaver'
2013-09-13 15:52:13 -05:00
],
function (angular, $, kbn, _, config, moment, Modernizr) {
2013-09-13 15:52:13 -05:00
'use strict';
var module = angular.module('kibana.services');
module.service('dashboard', function($routeParams, $http, $rootScope, $injector, $location,
2013-09-13 15:52:13 -05:00
ejsResource, timer, kbnIndex, alertSrv
) {
// A hash of defaults to use when loading a dashboard
var _dash = {
title: "",
style: "dark",
editable: true,
failover: false,
2013-09-20 16:26:06 -05:00
panel_hints: true,
2013-09-13 15:52:13 -05:00
rows: [],
services: {},
loader: {
save_gist: false,
save_elasticsearch: true,
save_local: true,
save_default: true,
save_temp: true,
save_temp_ttl_enable: true,
save_temp_ttl: '30d',
load_gist: true,
load_elasticsearch: true,
load_elasticsearch_size: 20,
load_local: true,
hide: false
},
index: {
interval: 'none',
pattern: '_all',
default: 'INDEX_MISSING'
},
};
// An elasticJS client to use
var ejs = ejsResource(config.elasticsearch);
var gist_pattern = /(^\d{5,}$)|(^[a-z0-9]{10,}$)|(gist.github.com(\/*.*)\/[a-z0-9]{5,}\/*$)/;
// Store a reference to this
var self = this;
var filterSrv,querySrv;
this.current = _.clone(_dash);
this.last = {};
$rootScope.$on('$routeChangeSuccess',function(){
// Clear the current dashboard to prevent reloading
self.current = {};
self.indices = [];
route();
});
var route = function() {
// Is there a dashboard type and id in the URL?
if(!(_.isUndefined($routeParams.kbnType)) && !(_.isUndefined($routeParams.kbnId))) {
var _type = $routeParams.kbnType;
var _id = $routeParams.kbnId;
switch(_type) {
case ('elasticsearch'):
self.elasticsearch_load('dashboard',_id);
break;
case ('temp'):
self.elasticsearch_load('temp',_id);
break;
case ('file'):
self.file_load(_id);
break;
case('script'):
self.script_load(_id);
break;
default:
self.file_load('default.json');
}
// No dashboard in the URL
} else {
// Check if browser supports localstorage, and if there's a dashboard
if (Modernizr.localstorage &&
!(_.isUndefined(window.localStorage['dashboard'])) &&
window.localStorage['dashboard'] !== ''
) {
var dashboard = JSON.parse(window.localStorage['dashboard']);
self.dash_load(dashboard);
// No? Ok, grab default.json, its all we have now
} else {
self.file_load('default.json');
}
}
};
// Since the dashboard is responsible for index computation, we can compute and assign the indices
// here before telling the panels to refresh
this.refresh = function() {
if(self.current.index.interval !== 'none') {
if(filterSrv.idsByType('time').length > 0) {
var _range = filterSrv.timeRange('min');
kbnIndex.indices(_range.from,_range.to,
self.current.index.pattern,self.current.index.interval
).then(function (p) {
if(p.length > 0) {
self.indices = p;
} else {
// Option to not failover
2013-09-13 15:52:13 -05:00
if(self.current.failover) {
self.indices = [self.current.index.default];
} else {
// Do not issue refresh if no indices match. This should be removed when panels
// properly understand when no indices are present
alertSrv.set('No results','There were no results because no indices were found that match your'+
' selected time span','info',5000);
2013-09-13 15:52:13 -05:00
return false;
}
}
$rootScope.$broadcast('refresh');
});
} else {
if(self.current.failover) {
self.indices = [self.current.index.default];
$rootScope.$broadcast('refresh');
} else {
alertSrv.set("No time filter",
'Timestamped indices are configured without a failover. Waiting for time filter.',
'info',5000);
}
}
} else {
self.indices = [self.current.index.default];
$rootScope.$broadcast('refresh');
}
};
var dash_defaults = function(dashboard) {
_.defaults(dashboard,_dash);
_.defaults(dashboard.index,_dash.index);
_.defaults(dashboard.loader,_dash.loader);
return dashboard;
};
this.dash_load = function(dashboard) {
// Cancel all timers
timer.cancel_all();
// Make sure the dashboard being loaded has everything required
dashboard = dash_defaults(dashboard);
// If not using time based indices, use the default index
if(dashboard.index.interval === 'none') {
self.indices = [dashboard.index.default];
}
self.current = _.clone(dashboard);
// Ok, now that we've setup the current dashboard, we can inject our services
querySrv = $injector.get('querySrv');
filterSrv = $injector.get('filterSrv');
// Make sure these re-init
querySrv.init();
filterSrv.init();
// If there's an index interval set and no existing time filter, send a refresh to set one
if(dashboard.index.interval !== 'none' && filterSrv.idsByType('time').length === 0) {
self.refresh();
}
return true;
};
this.gist_id = function(string) {
if(self.is_gist(string)) {
return string.match(gist_pattern)[0].replace(/.*\//, '');
}
};
this.is_gist = function(string) {
if(!_.isUndefined(string) && string !== '' && !_.isNull(string.match(gist_pattern))) {
return string.match(gist_pattern).length > 0 ? true : false;
} else {
return false;
}
};
this.to_file = function() {
var blob = new Blob([angular.toJson(self.current,true)], {type: "application/json;charset=utf-8"});
// from filesaver.js
window.saveAs(blob, self.current.title+"-"+new Date().getTime());
return true;
};
this.set_default = function(dashboard) {
if (Modernizr.localstorage) {
window.localStorage['dashboard'] = angular.toJson(dashboard || self.current);
$location.path('/dashboard');
2013-09-13 15:52:13 -05:00
return true;
} else {
return false;
}
};
this.purge_default = function() {
if (Modernizr.localstorage) {
window.localStorage['dashboard'] = '';
return true;
} else {
return false;
}
};
// TOFIX: Pretty sure this breaks when you're on a saved dashboard already
this.share_link = function(title,type,id) {
return {
location : window.location.href.replace(window.location.hash,""),
type : type,
id : id,
link : window.location.href.replace(window.location.hash,"")+"#dashboard/"+type+"/"+id,
title : title
};
};
var renderTemplate = function(json,params) {
var _r;
_.templateSettings = {interpolate : /\{\{(.+?)\}\}/g};
var template = _.template(json);
var rendered = template({ARGS:params});
try {
_r = angular.fromJson(rendered);
} catch(e) {
_r = false;
}
return _r;
};
this.file_load = function(file) {
return $http({
2013-09-17 11:56:12 -05:00
url: "app/dashboards/"+file+'?' + new Date().getTime(),
2013-09-13 15:52:13 -05:00
method: "GET",
transformResponse: function(response) {
return renderTemplate(response,$routeParams);
}
}).then(function(result) {
if(!result) {
return false;
}
self.dash_load(dash_defaults(result.data));
return true;
},function() {
alertSrv.set('Error',"Could not load <i>dashboards/"+file+"</i>. Please make sure it exists" ,'error');
return false;
});
};
this.elasticsearch_load = function(type,id) {
return $http({
url: config.elasticsearch + "/" + config.kibana_index + "/"+type+"/"+id,
method: "GET",
transformResponse: function(response) {
return renderTemplate(angular.fromJson(response)._source.dashboard, $routeParams);
}
}).error(function(data, status) {
if(status === 0) {
alertSrv.set('Error',"Could not contact Elasticsearch at "+config.elasticsearch+
". Please ensure that Elasticsearch is reachable from your system." ,'error');
} else {
alertSrv.set('Error',"Could not find "+id+". If you"+
" are using a proxy, ensure it is configured correctly",'error');
}
return false;
}).success(function(data) {
self.dash_load(data);
});
};
this.script_load = function(file) {
return $http({
url: "app/dashboards/"+file,
2013-09-13 15:52:13 -05:00
method: "GET",
transformResponse: function(response) {
/*jshint -W054 */
var _f = new Function('ARGS','kbn','_','moment','window','document','angular','require','define','$','jQuery',response);
return _f($routeParams,kbn,_,moment);
2013-09-13 15:52:13 -05:00
}
}).then(function(result) {
if(!result) {
return false;
}
self.dash_load(dash_defaults(result.data));
return true;
},function() {
alertSrv.set('Error',
"Could not load <i>scripts/"+file+"</i>. Please make sure it exists and returns a valid dashboard" ,
'error');
return false;
});
};
this.elasticsearch_save = function(type,title,ttl) {
// Clone object so we can modify it without influencing the existing obejct
var save = _.clone(self.current);
var id;
// Change title on object clone
if (type === 'dashboard') {
id = save.title = _.isUndefined(title) ? self.current.title : title;
}
// Create request with id as title. Rethink this.
var request = ejs.Document(config.kibana_index,type,id).source({
user: 'guest',
group: 'guest',
title: save.title,
dashboard: angular.toJson(save)
});
request = type === 'temp' && ttl ? request.ttl(ttl) : request;
return request.doIndex(
// Success
function(result) {
if(type === 'dashboard') {
$location.path('/dashboard/elasticsearch/'+title);
}
2013-09-13 15:52:13 -05:00
return result;
},
// Failure
function() {
return false;
}
);
};
this.elasticsearch_delete = function(id) {
return ejs.Document(config.kibana_index,'dashboard',id).doDelete(
// Success
function(result) {
return result;
},
// Failure
function() {
return false;
}
);
};
this.elasticsearch_list = function(query,count) {
var request = ejs.Request().indices(config.kibana_index).types('dashboard');
return request.query(
ejs.QueryStringQuery(query || '*')
).size(count).doSearch(
// Success
function(result) {
return result;
},
// Failure
function() {
return false;
}
);
};
this.save_gist = function(title,dashboard) {
var save = _.clone(dashboard || self.current);
save.title = title || self.current.title;
return $http({
url: "https://api.github.com/gists",
method: "POST",
data: {
"description": save.title,
"public": false,
"files": {
"kibana-dashboard.json": {
"content": angular.toJson(save,true)
}
}
}
}).then(function(data) {
return data.data.html_url;
}, function() {
return false;
});
};
this.gist_list = function(id) {
return $http.jsonp("https://api.github.com/gists/"+id+"?callback=JSON_CALLBACK"
).then(function(response) {
var files = [];
_.each(response.data.data.files,function(v) {
try {
var file = JSON.parse(v.content);
files.push(file);
} catch(e) {
return false;
}
});
return files;
}, function() {
return false;
});
};
});
});