mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
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:
parent
40bbd501ac
commit
fa8a913e0a
23 changed files with 339 additions and 43 deletions
1
.jshintignore
Normal file
1
.jshintignore
Normal file
|
@ -0,0 +1 @@
|
|||
vendor
|
3
common/.bowerrc
Normal file
3
common/.bowerrc
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"directory": "vendor"
|
||||
}
|
|
@ -76,4 +76,4 @@ define(['settings'],
|
|||
* Built on:
|
||||
* marvel commit: @@MARVEL_REVISION
|
||||
* kibana commit: @@KIBANA_REVISION
|
||||
*/
|
||||
*/
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>"
|
||||
};
|
||||
|
||||
|
|
9
kibana/panels/navigation/lib/extractIds.js
Normal file
9
kibana/panels/navigation/lib/extractIds.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
define(function () {
|
||||
'use strict';
|
||||
return function extractIds (link) {
|
||||
var matches = link.url.match(/([^\/]+.json)$/);
|
||||
if (matches) {
|
||||
return matches[1];
|
||||
}
|
||||
};
|
||||
});
|
11
kibana/panels/navigation/lib/filterLinks.js
Normal file
11
kibana/panels/navigation/lib/filterLinks.js
Normal 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;
|
||||
};
|
||||
};
|
||||
});
|
8
kibana/panels/navigation/lib/findDashboardById.js
Normal file
8
kibana/panels/navigation/lib/findDashboardById.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
define(function () {
|
||||
'use strict';
|
||||
return function (id) {
|
||||
return function findDashboardById (dashboard) {
|
||||
return dashboard._source.dashboard.base.id === id;
|
||||
};
|
||||
};
|
||||
});
|
20
kibana/panels/navigation/lib/loadDashboards.js
Normal file
20
kibana/panels/navigation/lib/loadDashboards.js
Normal 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;
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
19
kibana/panels/navigation/lib/mergeLinksWithDashboards.js
Normal file
19
kibana/panels/navigation/lib/mergeLinksWithDashboards.js
Normal 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;
|
||||
};
|
||||
};
|
||||
});
|
11
kibana/panels/navigation/lib/parseDashboard.js
Normal file
11
kibana/panels/navigation/lib/parseDashboard.js
Normal 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;
|
||||
};
|
||||
});
|
|
@ -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">
|
||||
|
|
|
@ -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));
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
4
kibana/panels/save/editor.html
Normal file
4
kibana/panels/save/editor.html
Normal 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>
|
17
kibana/panels/save/lib/save.js
Normal file
17
kibana/panels/save/lib/save.js
Normal 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;
|
||||
});
|
||||
};
|
||||
});
|
20
kibana/panels/save/module.html
Normal file
20
kibana/panels/save/module.html
Normal 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>
|
87
kibana/panels/save/module.js
Normal file
87
kibana/panels/save/module.js
Normal 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);
|
||||
// }
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -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([
|
|||
|
||||
|
||||
})
|
||||
;
|
||||
;
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue