Save Dashboards to Elasticsearch

- Added "show_home" option to loader
- Added custom "Save" panel to navigation panels
- Changed "Marvel Dashboards" to "Dashboards"
- Added merge code to Navigation to merge saved dashboards into static
  dashboards
- Splitting up functions into discrete files
- Removing findDashboardByLink() method
- Changed Experimental to Stable for all Marvel panels
This commit is contained in:
Chris Cowan 2014-03-18 15:41:50 -07:00
parent 40bbd501ac
commit fa8a913e0a
23 changed files with 339 additions and 43 deletions

1
.jshintignore Normal file
View file

@ -0,0 +1 @@
vendor

3
common/.bowerrc Normal file
View file

@ -0,0 +1,3 @@
{
"directory": "vendor"
}

View file

@ -76,4 +76,4 @@ define(['settings'],
* Built on:
* marvel commit: @@MARVEL_REVISION
* kibana commit: @@KIBANA_REVISION
*/
*/

View file

@ -281,22 +281,29 @@
{
"type": "marvel.navigation",
"source": "url",
"url": "../common/marvelLinks.json"
"url": "../common/marvelLinks.json",
"editable": false
},
{
"type": "marvel.save",
"editable": true,
"title": "Restore"
}
],
"loader": {
"show_home": false,
"save_gist": false,
"save_elasticsearch": true,
"save_local": true,
"save_default": true,
"save_temp": true,
"save_elasticsearch": false,
"save_local": false,
"save_default": false,
"save_temp": false,
"save_temp_ttl_enable": true,
"save_temp_ttl": "30d",
"load_gist": false,
"load_elasticsearch": true,
"load_elasticsearch": false,
"load_elasticsearch_size": 20,
"load_local": false,
"hide": false
"hide": true
},
"refresh": "1m"
}
}

View file

@ -408,5 +408,21 @@ dashboard.pulldowns = [
}
];
dashboard.loader = {
"show_home": false,
"save_gist": false,
"save_elasticsearch": false,
"save_local": false,
"save_default": false,
"save_temp": false,
"save_temp_ttl_enable": true,
"save_temp_ttl": "30d",
"load_gist": false,
"load_elasticsearch": false,
"load_elasticsearch_size": 20,
"load_local": false,
"hide": true
};
// Now return the object and we're good!
return dashboard;

View file

@ -704,5 +704,21 @@ dashboard.pulldowns = [
}
];
dashboard.loader = {
"show_home": false,
"save_gist": false,
"save_elasticsearch": false,
"save_local": false,
"save_default": false,
"save_temp": false,
"save_temp_ttl_enable": true,
"save_temp_ttl": "30d",
"load_gist": false,
"load_elasticsearch": false,
"load_elasticsearch_size": 20,
"load_local": false,
"hide": true
};
// Now return the object and we're good!
return dashboard;

View file

@ -408,22 +408,30 @@
{
"type": "marvel.navigation",
"source": "url",
"url": "../common/marvelLinks.json"
"url": "../common/marvelLinks.json",
"editable": false
},
{
"type": "marvel.save",
"editable": true,
"title": "Restore"
}
],
"loader": {
"show_home": false,
"save_gist": false,
"save_elasticsearch": true,
"save_local": true,
"save_default": true,
"save_temp": true,
"save_temp_ttl_enable": true,
"save_elasticsearch": false,
"save_local": false,
"save_default": false,
"save_temp": false,
"save_temp_ttl_enable": false,
"save_temp_ttl": "30d",
"load_gist": false,
"load_elasticsearch": true,
"load_elasticsearch": false,
"load_elasticsearch_size": 20,
"load_local": true,
"hide": false
"load_local": false,
"hide": true
},
"refresh": "10s"
}
}

View file

@ -1306,6 +1306,7 @@
}
],
"loader": {
"show_home": false,
"save_gist": true,
"save_elasticsearch": true,
"save_local": true,
@ -1320,4 +1321,4 @@
"hide": false
},
"refresh": false
}
}

View file

@ -28,7 +28,7 @@ function (angular, app, kbn, _, ga) {
$scope.panelMeta = {
modals : [],
editorTabs : [],
status: "Experimental",
status: "Stable",
description: "A simple view of cluster health<p>"
};

View file

@ -0,0 +1,9 @@
define(function () {
'use strict';
return function extractIds (link) {
var matches = link.url.match(/([^\/]+.json)$/);
if (matches) {
return matches[1];
}
};
});

View file

@ -0,0 +1,11 @@
define(function (require) {
'use strict';
var $ = require("jquery");
var a = $('<a />');
return function (current) {
return function filterLinks (link) {
a.attr("href", link.url);
return a[0].href !== current;
};
};
});

View file

@ -0,0 +1,8 @@
define(function () {
'use strict';
return function (id) {
return function findDashboardById (dashboard) {
return dashboard._source.dashboard.base.id === id;
};
};
});

View file

@ -0,0 +1,20 @@
define(function (require) {
'use strict';
var config = require('config');
return function (client, ids) {
var url = config.elasticsearch+'/'+config.kibana_index+'/dashboard/_mget';
var body = {
ids: ids
};
return client.post(url, body).then(function (resp) {
return resp.data.docs.filter(function (row) {
// Elasticsearch 0.9.x uses exists instead of found so we need to check
// for that first to see if it exists. Otherwise use row.found.
if (row.exists != null) {
return row.exists;
}
return row.found;
});
});
};
});

View file

@ -0,0 +1,19 @@
define(function (require) {
'use strict';
var _ = require('lodash');
var findDashboardById = require('./findDashboardById');
return function (dashboards) {
return function mergeLinksWithDashboards (link) {
var id;
var matches = link.url.match(/([^\/]+\.json)$/);
if (matches) {
id = matches[1];
var repacement = _.find(dashboards, findDashboardById(id));
if (repacement) {
link.url = '../kibana/index.html#/dashboard/elasticsearch/'+encodeURIComponent(repacement._id);
}
}
return link;
};
};
});

View file

@ -0,0 +1,11 @@
define(function () {
'use strict';
return function parseDashboard (dashboard) {
try {
dashboard._source.dashboard = JSON.parse(dashboard._source.dashboard);
} catch (e) {
dashboard._source.dashboard = {};
}
return dashboard;
};
});

View file

@ -5,7 +5,7 @@
<ul class="nav nav-pills marvel-navigation-dropdown">
<li class="dropdown">
<a class="dropdown-toggle marvel-navigation-dropdown" data-toggle="dropdown" href="" data-placement="bottom" ng-click="dismiss();">
Marvel Dashboards <i ng-class="panel.icon"></i>
Dashboards <i ng-class="panel.icon"></i>
</a>
<ul class="dropdown-menu">

View file

@ -11,23 +11,32 @@
*
*/
define([
'angular',
'app',
'jquery',
'lodash',
'../../../../../common/analytics',
'factories/store'
],
function (angular, app, $, _, ga) {
define(function (require) {
'use strict';
var angular = require('angular');
var app = require('app');
var $ = require('jquery');
var _ = require('lodash');
var ga = require('../../../../../common/analytics');
var loadDashboards = require('./lib/loadDashboards');
var extractIds = require('./lib/extractIds');
var findDashboardById = require('./lib/findDashboardById');
var parseDashboard = require('./lib/parseDashboard');
var mergeLinksWithDashboards = require('./lib/mergeLinksWithDashboards');
var filterLinks = require('./lib/filterLinks');
require('factories/store');
var module = angular.module('kibana.panels.marvel.navigation', []);
app.useModule(module);
module.controller('marvel.navigation', function($scope, $http, storeFactory) {
module.controller('marvel.navigation', function($scope, $http, storeFactory, $q, dashboard, $location, $routeParams) {
$scope.panelMeta = {
status : "Experimental",
status : "Stable",
description : "A simple dropdown panel with a list of links"
};
@ -68,6 +77,9 @@ function (angular, app, $, _, ga) {
_.defaults($scope.panel,_d);
$scope.init = function() {
if($scope.panel.source === 'panel') {
$scope.links = $scope.panel.links;
@ -78,15 +90,41 @@ function (angular, app, $, _, ga) {
}
if($scope.panel.source === 'url') {
$http.get($scope.panel.url).then(function(response) {
var a = $('<a />');
$scope.links = _.filter(response.data.links, function (link) {
a.attr("href", link.url);
$http.get($scope.panel.url).then(function (response) {
var a = $('<a />');
var links = response.data.links;
var ids = links.filter(extractIds).map(extractIds);
loadDashboards($http, ids).then(function (dashboards) {
// Parse all the dashboards so we can check their base.id
dashboards = _.map(dashboards, parseDashboard);
// If the route is file based and the location matches a saved dashboard,
// we need to redirect the browser to the saved dashboard.
if ($routeParams.kbnType === 'file') {
// Find out if there is an override for the the current dashboard.
var overrideDashboard = _.find(dashboards, findDashboardById($routeParams.kbnId));
// Redirect the the override dashboard if it exists
if (overrideDashboard) {
var href = '../kibana/index.html#/dashboard/elasticsearch/'+
encodeURIComponent(overrideDashboard._id);
a.attr('href', href);
window.location = a[0].href;
return;
}
}
// Merge the links with the saved dashboards
links = _.map(links, mergeLinksWithDashboards(dashboards));
// Filter the current dashboard out of the navigation links
var current = window.location.href;
// remove parameters
current = current.replace(/\?.*$/,'');
return a[0].href !== current;
$scope.links = _.filter(links, filterLinks(current));
});
});
}

View file

@ -0,0 +1,4 @@
<div ng-init="init()" ng-controller="marvel.reset">
<p>Click the button below to restore the dashboard to it's original state.</p>
<button ng-click="reset();dismiss();" class="btn btn-info" ng-disabled="!saved">Restore Dashboard</button>
</div>

View file

@ -0,0 +1,17 @@
define(function () {
'use strict';
var config = require('config');
var angular = require('angular');
return function (client, dashboard) {
var url = config.elasticsearch+'/'+config.kibana_index+'/dashboard/'+dashboard.current.base.id;
var body = {
user: 'guest',
group: 'guest',
title: dashboard.current.title,
dashboard: angular.toJson(dashboard.current)
};
return client.put(url, body).then(function (resp) {
return resp.data;
});
};
});

View file

@ -0,0 +1,20 @@
<style type="text/css">
.navbar .nav>li>a.reset-menu {
display: inline-block;
padding: 0 0 11px 0;
margin: 18px 0 0 0;
float: right;
}
.navbar .nav>li>a.reset-menu:active {
background-color: none;
}
</style>
<div ng-controller='marvel.save' ng-init="init()">
<ul class="nav marvel-navigation-dropdown" style="margin: 0">
<li>
<a bs-tooltip="Save" ng-click="save()">
<i class="icon-save"></i>
</a>
</li>
</ul>
</div>

View file

@ -0,0 +1,87 @@
define(function (require) {
'use strict';
var angular = require('angular');
var app = require('app');
var _ = require('lodash');
var $ = require('jquery');
var save = require('./lib/save');
var module = angular.module('kibana.panels.marvel.save', []);
app.useModule(module);
module.controller('marvel.reset', function ($scope, dashboard, $routeParams) {
$scope.reset = function () {
var link = dashboard.current.base.link;
dashboard.elasticsearch_delete($routeParams.kbnId).then(function () {
var a = $('<a/>');
a.attr('href', '../kibana/index.html#'+link);
// Add an artificial wait for the index to update so when the page reloads
// it doesn't try and redirect to the saved dashboard.
setTimeout(function () {
window.location = a[0].href;
}, 1000);
});
};
$scope.init = function () {
$scope.saved = (($routeParams.kbnType === 'elasticsearch') && dashboard.current.base);
};
});
module.controller('marvel.save', function ($scope, dashboard, kbnVersion, $routeParams, alertSrv, $location, $http) {
$scope.panelMeta = {
status: 'Stable',
description: 'A simple panel for saving a Marvel dashboard'
};
$scope.panel = {
};
$scope.save = function () {
if ($routeParams.kbnType !== 'script') {
save($http, dashboard).then(function (result) {
$location.path('/dashboard/elasticsearch/'+result._id);
if(!_.isUndefined(result._id)) {
alertSrv.set('Dashboard Saved','This dashboard has been saved to Elasticsearch as "' +
result._id + '"','success',5000);
} else {
alertSrv.set('Save failed','Dashboard could not be saved to Elasticsearch','error',5000);
}
});
}
};
$scope.init = function () {
// We need to set a way to track the original source of the dashboard
var isFile = ( $routeParams.kbnType === 'file' );
var isMarvel = /^marvel.+$/.test($routeParams.kbnId);
// Only add the base object if the dashboard is loaded from the file
// and the current dashboard is a marvel dashboard.
if (isFile && isMarvel) {
dashboard.current.base = {
id: $routeParams.kbnId,
version: kbnVersion,
link: $location.path()
};
}
if (!isFile && dashboard.current.base) {
$scope.saved = true;
}
// TODO: WE need to discuse our strategy for upgrades to Marvel. Are we
// gonna reset their saved dashboards? Or do we leave them in tact?
// if (dashboard.current.base.version !== kbnVersion) {
// dashboard.purge_default();
// dashboard.elasticsearch_delete($routeParams.kbnId);
// }
};
});
});

View file

@ -46,7 +46,7 @@ define([
$scope.panelMeta = {
modals: [],
editorTabs: [],
status: "Experimental",
status: "Stable",
description: "A stats table for nodes or nodes"
};
@ -1181,4 +1181,4 @@ define([
})
;
;

View file

@ -22,7 +22,7 @@
<ul class="nav pull-right">
<li id="nav_btn" class="dropdown">
<a href="#" title="Goto" class="dropdown-toggle" data-toggle="dropdown">
Marvel Dashboards <b class="caret"></b>
Dashboards <b class="caret"></b>
</a>
<ul class="dropdown-menu" role="menu">
</ul>
@ -220,4 +220,4 @@ require(['require','ace'], function (require) { require(['app'], function () {})
</script>
</body>
</html>
</html>