Commit b3be51f1 by Torkel Ödegaard

Lots of work on search and dashboard loading, trying to generalize concepts and code, #960

parent a40299b4
...@@ -100,6 +100,7 @@ func Register(r *macaron.Macaron) { ...@@ -100,6 +100,7 @@ func Register(r *macaron.Macaron) {
r.Group("/dashboards", func() { r.Group("/dashboards", func() {
r.Combo("/db/:slug").Get(GetDashboard).Delete(DeleteDashboard) r.Combo("/db/:slug").Get(GetDashboard).Delete(DeleteDashboard)
r.Post("/db", reqEditorRole, bind(m.SaveDashboardCommand{}), PostDashboard) r.Post("/db", reqEditorRole, bind(m.SaveDashboardCommand{}), PostDashboard)
r.Get("/file/:file", GetDashboardFromJsonFile)
r.Get("/home", GetHomeDashboard) r.Get("/home", GetHomeDashboard)
}) })
......
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/metrics"
"github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/search"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
) )
...@@ -48,7 +49,7 @@ func GetDashboard(c *middleware.Context) { ...@@ -48,7 +49,7 @@ func GetDashboard(c *middleware.Context) {
dash := query.Result dash := query.Result
dto := dtos.DashboardFullWithMeta{ dto := dtos.DashboardFullWithMeta{
Dashboard: dash.Data, Dashboard: dash.Data,
Meta: dtos.DashboardMeta{IsStarred: isStarred, Slug: slug}, Meta: dtos.DashboardMeta{IsStarred: isStarred, Slug: slug, Type: m.DashTypeDB},
} }
c.JSON(200, dto) c.JSON(200, dto)
...@@ -118,3 +119,19 @@ func GetHomeDashboard(c *middleware.Context) { ...@@ -118,3 +119,19 @@ func GetHomeDashboard(c *middleware.Context) {
c.JSON(200, &dash) c.JSON(200, &dash)
} }
func GetDashboardFromJsonFile(c *middleware.Context) {
file := c.Params(":file")
dashboard := search.GetDashboardFromJsonIndex(file)
if dashboard == nil {
c.JsonApiErr(404, "Dashboard not found", nil)
return
}
dash := dtos.DashboardFullWithMeta{Dashboard: dashboard.Data}
dash.Meta.Type = m.DashTypeJson
dash.Meta.CanSave = false
c.JSON(200, &dash)
}
...@@ -32,6 +32,8 @@ type DashboardMeta struct { ...@@ -32,6 +32,8 @@ type DashboardMeta struct {
IsStarred bool `json:"isStarred,omitempty"` IsStarred bool `json:"isStarred,omitempty"`
IsHome bool `json:"isHome,omitempty"` IsHome bool `json:"isHome,omitempty"`
IsSnapshot bool `json:"isSnapshot,omitempty"` IsSnapshot bool `json:"isSnapshot,omitempty"`
Type string `json:"type,omitempty"`
CanSave bool `json:"canSave"`
Slug string `json:"slug"` Slug string `json:"slug"`
Expires time.Time `json:"expires"` Expires time.Time `json:"expires"`
Created time.Time `json:"created"` Created time.Time `json:"created"`
......
...@@ -15,6 +15,12 @@ var ( ...@@ -15,6 +15,12 @@ var (
ErrDashboardVersionMismatch = errors.New("The dashboard has been changed by someone else") ErrDashboardVersionMismatch = errors.New("The dashboard has been changed by someone else")
) )
var (
DashTypeJson = "file"
DashTypeDB = "db"
DashTypeScript = "script"
)
// Dashboard model // Dashboard model
type Dashboard struct { type Dashboard struct {
Id int64 Id int64
......
...@@ -9,7 +9,8 @@ type SearchResult struct { ...@@ -9,7 +9,8 @@ type SearchResult struct {
type DashboardSearchHit struct { type DashboardSearchHit struct {
Id int64 `json:"id"` Id int64 `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Slug string `json:"slug"` Uri string `json:"uri"`
Type string `json:"type"`
Tags []string `json:"tags"` Tags []string `json:"tags"`
IsStarred bool `json:"isStarred"` IsStarred bool `json:"isStarred"`
} }
......
...@@ -17,11 +17,10 @@ type JsonDashIndex struct { ...@@ -17,11 +17,10 @@ type JsonDashIndex struct {
} }
type JsonDashIndexItem struct { type JsonDashIndexItem struct {
Title string
TitleLower string TitleLower string
Tags []string
TagsCsv string TagsCsv string
FilePath string Path string
Dashboard *m.Dashboard
} }
func NewJsonDashIndex(path string, orgIds string) *JsonDashIndex { func NewJsonDashIndex(path string, orgIds string) *JsonDashIndex {
...@@ -40,9 +39,9 @@ func (index *JsonDashIndex) Search(query *Query) ([]*m.DashboardSearchHit, error ...@@ -40,9 +39,9 @@ func (index *JsonDashIndex) Search(query *Query) ([]*m.DashboardSearchHit, error
for _, item := range index.items { for _, item := range index.items {
if strings.Contains(item.TitleLower, query.Title) { if strings.Contains(item.TitleLower, query.Title) {
results = append(results, &m.DashboardSearchHit{ results = append(results, &m.DashboardSearchHit{
Title: item.Title, Title: item.Dashboard.Title,
Tags: item.Tags, Tags: item.Dashboard.GetTags(),
Slug: item.FilePath, Uri: "file/" + item.Path,
}) })
} }
} }
...@@ -50,6 +49,16 @@ func (index *JsonDashIndex) Search(query *Query) ([]*m.DashboardSearchHit, error ...@@ -50,6 +49,16 @@ func (index *JsonDashIndex) Search(query *Query) ([]*m.DashboardSearchHit, error
return results, nil return results, nil
} }
func (index *JsonDashIndex) GetDashboard(path string) *m.Dashboard {
for _, item := range index.items {
if item.Path == path {
return item.Dashboard
}
}
return nil
}
func (index *JsonDashIndex) updateIndex() error { func (index *JsonDashIndex) updateIndex() error {
log.Info("Updating JSON dashboard index, path: %v", index.path) log.Info("Updating JSON dashboard index, path: %v", index.path)
...@@ -102,13 +111,13 @@ func loadDashboardFromFile(filename string) (*JsonDashIndexItem, error) { ...@@ -102,13 +111,13 @@ func loadDashboardFromFile(filename string) (*JsonDashIndexItem, error) {
return nil, err return nil, err
} }
dash := m.NewDashboardFromJson(data) stat, _ := os.Stat(filename)
item := &JsonDashIndexItem{} item := &JsonDashIndexItem{}
item.Title = dash.Title item.Dashboard = m.NewDashboardFromJson(data)
item.TitleLower = strings.ToLower(item.Title) item.TitleLower = strings.ToLower(item.Dashboard.Title)
item.Tags = dash.GetTags() item.TagsCsv = strings.Join(item.Dashboard.GetTags(), ",")
item.TagsCsv = strings.Join(item.Tags, ",") item.Path = stat.Name()
return item, nil return item, nil
} }
...@@ -87,3 +87,10 @@ func setIsStarredFlagOnSearchResults(userId int64, hits []*m.DashboardSearchHit) ...@@ -87,3 +87,10 @@ func setIsStarredFlagOnSearchResults(userId int64, hits []*m.DashboardSearchHit)
return nil return nil
} }
func GetDashboardFromJsonIndex(filename string) *m.Dashboard {
if jsonDashIndex == nil {
return nil
}
return jsonDashIndex.GetDashboard(filename)
}
...@@ -175,7 +175,7 @@ func SearchDashboards(query *m.SearchDashboardsQuery) error { ...@@ -175,7 +175,7 @@ func SearchDashboards(query *m.SearchDashboardsQuery) error {
hit = &m.DashboardSearchHit{ hit = &m.DashboardSearchHit{
Id: item.Id, Id: item.Id,
Title: item.Title, Title: item.Title,
Slug: item.Slug, Uri: "db/" + item.Slug,
Tags: []string{}, Tags: []string{},
} }
query.Result = append(query.Result, hit) query.Result = append(query.Result, hit)
......
...@@ -70,7 +70,7 @@ function (angular, _, config) { ...@@ -70,7 +70,7 @@ function (angular, _, config) {
$scope.resultCount = results.tagsOnly ? results.tags.length : results.dashboards.length; $scope.resultCount = results.tagsOnly ? results.tags.length : results.dashboards.length;
$scope.results.tags = results.tags; $scope.results.tags = results.tags;
$scope.results.dashboards = _.map(results.dashboards, function(dash) { $scope.results.dashboards = _.map(results.dashboards, function(dash) {
dash.url = 'dashboard/db/' + dash.slug; dash.url = 'dashboard/' + dash.uri;
return dash; return dash;
}); });
......
...@@ -22,6 +22,7 @@ function (angular, _, kbn, $) { ...@@ -22,6 +22,7 @@ function (angular, _, kbn, $) {
}; };
this.setTimeRenderStart = function(scope) { this.setTimeRenderStart = function(scope) {
scope.timing = scope.timing || {};
scope.timing.renderStart = new Date().getTime(); scope.timing.renderStart = new Date().getTime();
}; };
......
<grafana-panel> <grafana-panel>
<div class="dashlist"> <div class="dashlist">
<div class="dashlist-item" ng-repeat="dash in dashList"> <div class="dashlist-item" ng-repeat="dash in dashList">
<a class="dashlist-link" href="dashboard/db/{{dash.slug}}"> <a class="dashlist-link" href="dashboard/{{dash.uri}}">
<span class="dashlist-title"> <span class="dashlist-title">
{{dash.title}} {{dash.title}}
</span> </span>
......
...@@ -12,22 +12,7 @@ define([ ...@@ -12,22 +12,7 @@ define([
$routeProvider $routeProvider
.when('/', { .when('/', {
templateUrl: 'app/partials/dashboard.html', templateUrl: 'app/partials/dashboard.html',
controller : 'DashFromDBCtrl', controller : 'LoadDashboardCtrl',
reloadOnSearch: false,
})
.when('/dashboard/db/:slug', {
templateUrl: 'app/partials/dashboard.html',
controller : 'DashFromDBCtrl',
reloadOnSearch: false,
})
.when('/dashboard/file/:jsonFile', {
templateUrl: 'app/partials/dashboard.html',
controller : 'DashFromFileCtrl',
reloadOnSearch: false,
})
.when('/dashboard/script/:jsFile', {
templateUrl: 'app/partials/dashboard.html',
controller : 'DashFromScriptCtrl',
reloadOnSearch: false, reloadOnSearch: false,
}) })
.when('/dashboard/import/:file', { .when('/dashboard/import/:file', {
...@@ -35,9 +20,10 @@ define([ ...@@ -35,9 +20,10 @@ define([
controller : 'DashFromImportCtrl', controller : 'DashFromImportCtrl',
reloadOnSearch: false, reloadOnSearch: false,
}) })
.when('/dashboard/snapshot/:key', { .when('/dashboard/:type/:slug', {
templateUrl: 'app/partials/dashboard.html', templateUrl: 'app/partials/dashboard.html',
controller : 'DashFromSnapshotCtrl', controller : 'LoadDashboardCtrl',
reloadOnSearch: false,
}) })
.when('/dashboard/solo/db/:slug', { .when('/dashboard/solo/db/:slug', {
templateUrl: 'app/features/panel/partials/soloPanel.html', templateUrl: 'app/features/panel/partials/soloPanel.html',
......
...@@ -10,12 +10,14 @@ function (angular, _, kbn, moment, $) { ...@@ -10,12 +10,14 @@ function (angular, _, kbn, moment, $) {
var module = angular.module('grafana.routes'); var module = angular.module('grafana.routes');
module.controller('DashFromDBCtrl', function($scope, $routeParams, backendSrv) { module.controller('LoadDashboardCtrl', function(
$scope, $routeParams, backendSrv, dashboardSrv, datasourceSrv, $http, $q, $timeout, contextSrv) {
function dashboardLoadFailed(title) { function dashboardLoadFailed(title) {
$scope.initDashboard({meta: {}, dashboard: {title: title}}, $scope); $scope.initDashboard({meta: {}, dashboard: {title: title}}, $scope);
} }
// Home dashboard
if (!$routeParams.slug) { if (!$routeParams.slug) {
backendSrv.get('/api/dashboards/home').then(function(result) { backendSrv.get('/api/dashboards/home').then(function(result) {
var meta = result.meta; var meta = result.meta;
...@@ -24,90 +26,10 @@ function (angular, _, kbn, moment, $) { ...@@ -24,90 +26,10 @@ function (angular, _, kbn, moment, $) {
},function() { },function() {
dashboardLoadFailed('Not found'); dashboardLoadFailed('Not found');
}); });
return;
}
return backendSrv.getDashboard($routeParams.slug).then(function(result) {
$scope.initDashboard(result, $scope);
}, function() {
dashboardLoadFailed('Not found');
});
});
module.controller('DashFromSnapshotCtrl', function($scope, $routeParams, backendSrv, contextSrv) {
//don't show the sidemenu in snapshots.
contextSrv.sidemenu = false;
backendSrv.get('/api/snapshots/' + $routeParams.key).then(function(result) {
$scope.initDashboard(result, $scope);
}, function() {
$scope.initDashboard({
meta: {
isSnapshot: true,
canSave: false,
canEdit: false,
},
dashboard: {
title: 'Snapshot not found'
}
}, $scope);
});
});
module.controller('DashFromImportCtrl', function($scope, $location, alertSrv) {
if (!window.grafanaImportDashboard) {
alertSrv.set('Not found', 'Cannot reload page with unsaved imported dashboard', 'warning', 7000);
$location.path('');
return; return;
} }
$scope.initDashboard({
meta: { canShare: false, canStar: false },
dashboard: window.grafanaImportDashboard
}, $scope);
});
module.controller('NewDashboardCtrl', function($scope) {
$scope.initDashboard({
meta: { canStar: false, canShare: false },
dashboard: {
title: "New dashboard",
rows: [{ height: '250px', panels:[] }]
},
}, $scope);
});
module.controller('DashFromFileCtrl', function($scope, $rootScope, $http, $routeParams) {
var file_load = function(file) {
return $http({
url: "public/dashboards/"+file.replace(/\.(?!json)/,"/")+'?' + new Date().getTime(),
method: "GET",
transformResponse: function(response) {
return angular.fromJson(response);
}
}).then(function(result) {
if(!result) {
return false;
}
return result.data;
},function() {
$scope.appEvent('alert-error', ["Dashboard load failed", "Could not load "+file+". Please make sure it exists"]);
return false;
});
};
file_load($routeParams.jsonFile).then(function(result) {
$scope.initDashboard({
meta: { canSave: false, canDelete: false },
dashboard: result
}, $scope);
});
});
module.controller('DashFromScriptCtrl', function($scope, $rootScope, $http, $routeParams, $q, dashboardSrv, datasourceSrv, $timeout) {
// Scripted dashboards
var execute_script = function(result) { var execute_script = function(result) {
var services = { var services = {
dashboardSrv: dashboardSrv, dashboardSrv: dashboardSrv,
...@@ -145,13 +67,58 @@ function (angular, _, kbn, moment, $) { ...@@ -145,13 +67,58 @@ function (angular, _, kbn, moment, $) {
}); });
}; };
script_load($routeParams.jsFile).then(function(result) { function loadScriptedDashboard() {
script_load($routeParams.slug).then(function(result) {
$scope.initDashboard({ $scope.initDashboard({
meta: {fromScript: true, canDelete: false, canSave: false}, meta: {fromScript: true, canDelete: false, canSave: false},
dashboard: result.data dashboard: result.data
}, $scope); }, $scope);
}); });
}
if ($routeParams.type === 'script') {
loadScriptedDashboard();
return;
}
if ($routeParams.type === 'snapshot') {
contextSrv.sidemenu = false;
backendSrv.get('/api/snapshots/' + $routeParams.slug).then(function(result) {
$scope.initDashboard(result, $scope);
}, function() {
$scope.initDashboard({meta:{isSnapshot: true, canSave: false, canEdit: false}, dashboard: {title: 'Snapshot not found'}}, $scope);
});
return;
}
return backendSrv.getDashboard($routeParams.type, $routeParams.slug).then(function(result) {
$scope.initDashboard(result, $scope);
}, function() {
dashboardLoadFailed('Not found');
});
});
module.controller('DashFromImportCtrl', function($scope, $location, alertSrv) {
if (!window.grafanaImportDashboard) {
alertSrv.set('Not found', 'Cannot reload page with unsaved imported dashboard', 'warning', 7000);
$location.path('');
return;
}
$scope.initDashboard({
meta: { canShare: false, canStar: false },
dashboard: window.grafanaImportDashboard
}, $scope);
});
module.controller('NewDashboardCtrl', function($scope) {
$scope.initDashboard({
meta: { canStar: false, canShare: false },
dashboard: {
title: "New dashboard",
rows: [{ height: '250px', panels:[] }]
},
}, $scope);
}); });
}); });
...@@ -119,8 +119,8 @@ function (angular, _, config) { ...@@ -119,8 +119,8 @@ function (angular, _, config) {
return this.get('/api/search', query); return this.get('/api/search', query);
}; };
this.getDashboard = function(slug) { this.getDashboard = function(type, slug) {
return this.get('/api/dashboards/db/' + slug); return this.get('/api/dashboards/' + type + '/' + slug);
}; };
this.saveDashboard = function(dash, options) { this.saveDashboard = function(dash, options) {
......
{
"title": "New Dashboard",
"time": {
"from": "now-6h",
"to": "now"
},
"templating": {
"list": []
},
"rows": [
{
"title": "Row1",
"height": "250px",
"editable": true,
"collapse": false,
"panels": []
}
],
"editable": true,
"style": "dark",
"nav": [
{
"type": "timepicker",
"enable": true,
"status": "Stable",
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
],
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"now": true
}
],
"refresh": false
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment