Commit 0398face by Torkel Ödegaard

feat(plugins): dashboard import for data sources is working! #4298

parent 3fb0b718
...@@ -53,7 +53,6 @@ func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapsho ...@@ -53,7 +53,6 @@ func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapsho
} }
func GetDashboardSnapshot(c *middleware.Context) { func GetDashboardSnapshot(c *middleware.Context) {
key := c.Params(":key") key := c.Params(":key")
query := &m.GetDashboardSnapshotQuery{Key: key} query := &m.GetDashboardSnapshotQuery{Key: key}
...@@ -136,5 +135,4 @@ func SearchDashboardSnapshots(c *middleware.Context) Response { ...@@ -136,5 +135,4 @@ func SearchDashboardSnapshots(c *middleware.Context) Response {
} }
return Json(200, dtos) return Json(200, dtos)
//return Json(200, searchQuery.Result)
} }
...@@ -54,7 +54,7 @@ type DashboardMeta struct { ...@@ -54,7 +54,7 @@ type DashboardMeta struct {
type DashboardFullWithMeta struct { type DashboardFullWithMeta struct {
Meta DashboardMeta `json:"meta"` Meta DashboardMeta `json:"meta"`
Dashboard map[string]interface{} `json:"dashboard"` Dashboard *simplejson.Json `json:"dashboard"`
} }
type DataSource struct { type DataSource struct {
......
...@@ -6,7 +6,6 @@ import ( ...@@ -6,7 +6,6 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"io" "io"
"reflect" "reflect"
"strconv" "strconv"
...@@ -14,7 +13,6 @@ import ( ...@@ -14,7 +13,6 @@ import (
// Implements the json.Unmarshaler interface. // Implements the json.Unmarshaler interface.
func (j *Json) UnmarshalJSON(p []byte) error { func (j *Json) UnmarshalJSON(p []byte) error {
fmt.Printf("UnmarshalJSON")
dec := json.NewDecoder(bytes.NewBuffer(p)) dec := json.NewDecoder(bytes.NewBuffer(p))
dec.UseNumber() dec.UseNumber()
return dec.Decode(&j.data) return dec.Decode(&j.data)
......
package models package models
import "time" import (
"time"
"github.com/grafana/grafana/pkg/components/simplejson"
)
// DashboardSnapshot model // DashboardSnapshot model
type DashboardSnapshot struct { type DashboardSnapshot struct {
...@@ -17,7 +21,7 @@ type DashboardSnapshot struct { ...@@ -17,7 +21,7 @@ type DashboardSnapshot struct {
Created time.Time Created time.Time
Updated time.Time Updated time.Time
Dashboard map[string]interface{} Dashboard *simplejson.Json
} }
// DashboardSnapshotDTO without dashboard map // DashboardSnapshotDTO without dashboard map
...@@ -40,7 +44,7 @@ type DashboardSnapshotDTO struct { ...@@ -40,7 +44,7 @@ type DashboardSnapshotDTO struct {
// COMMANDS // COMMANDS
type CreateDashboardSnapshotCommand struct { type CreateDashboardSnapshotCommand struct {
Dashboard map[string]interface{} `json:"dashboard" binding:"Required"` Dashboard *simplejson.Json `json:"dashboard" binding:"Required"`
Name string `json:"name" binding:"Required"` Name string `json:"name" binding:"Required"`
Expires int64 `json:"expires"` Expires int64 `json:"expires"`
......
package plugins package plugins
import ( import (
"encoding/json"
"fmt" "fmt"
"reflect"
"regexp" "regexp"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
...@@ -127,32 +127,45 @@ func (this *DashTemplateEvaluator) Eval() (*simplejson.Json, error) { ...@@ -127,32 +127,45 @@ func (this *DashTemplateEvaluator) Eval() (*simplejson.Json, error) {
log.Info("Import: dashboard has no __import section") log.Info("Import: dashboard has no __import section")
} }
this.EvalObject(this.template, this.result) return simplejson.NewFromAny(this.evalObject(this.template)), nil
return this.result, nil
} }
func (this *DashTemplateEvaluator) EvalObject(source *simplejson.Json, writer *simplejson.Json) { func (this *DashTemplateEvaluator) evalValue(source *simplejson.Json) interface{} {
for key, value := range source.MustMap() { sourceValue := source.Interface()
if key == "__inputs" {
continue
}
switch v := value.(type) { switch v := sourceValue.(type) {
case string: case string:
interpolated := this.varRegex.ReplaceAllStringFunc(v, func(match string) string { interpolated := this.varRegex.ReplaceAllStringFunc(v, func(match string) string {
return this.variables[match] return this.variables[match]
}) })
writer.Set(key, interpolated) return interpolated
case bool:
return v
case json.Number:
return v
case map[string]interface{}: case map[string]interface{}:
childSource := simplejson.NewFromAny(value) return this.evalObject(source)
childWriter := simplejson.New()
writer.Set(key, childWriter.Interface())
this.EvalObject(childSource, childWriter)
case []interface{}: case []interface{}:
default: array := make([]interface{}, 0)
log.Info("type: %v", reflect.TypeOf(value)) for _, item := range v {
log.Error(3, "Unknown json type key: %v , type: %v", key, value) array = append(array, this.evalValue(simplejson.NewFromAny(item)))
} }
return array
} }
return nil
}
func (this *DashTemplateEvaluator) evalObject(source *simplejson.Json) interface{} {
result := make(map[string]interface{})
for key, value := range source.MustMap() {
if key == "__inputs" {
continue
}
result[key] = this.evalValue(simplejson.NewFromAny(value))
}
return result
} }
package plugins package plugins
import ( import (
"io/ioutil"
"testing" "testing"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
...@@ -34,7 +35,7 @@ func TestDashboardImport(t *testing.T) { ...@@ -34,7 +35,7 @@ func TestDashboardImport(t *testing.T) {
OrgId: 1, OrgId: 1,
UserId: 1, UserId: 1,
Inputs: []ImportDashboardInput{ Inputs: []ImportDashboardInput{
{Name: "*", Type: "datasource"}, {Name: "*", Type: "datasource", Value: "graphite"},
}, },
} }
...@@ -44,11 +45,15 @@ func TestDashboardImport(t *testing.T) { ...@@ -44,11 +45,15 @@ func TestDashboardImport(t *testing.T) {
Convey("should install dashboard", func() { Convey("should install dashboard", func() {
So(importedDash, ShouldNotBeNil) So(importedDash, ShouldNotBeNil)
dashStr, _ := importedDash.Data.EncodePretty() resultStr, _ := importedDash.Data.EncodePretty()
So(string(dashStr), ShouldEqual, "") expectedBytes, _ := ioutil.ReadFile("../../tests/test-app/dashboards/connections_result.json")
expectedJson, _ := simplejson.NewJson(expectedBytes)
expectedStr, _ := expectedJson.EncodePretty()
// So(panel["datasource"], ShouldEqual, "graphite") So(string(resultStr), ShouldEqual, string(expectedStr))
// So(importedDash.Data["__inputs"], ShouldBeNil)
panel := importedDash.Data.Get("rows").GetIndex(0).Get("panels").GetIndex(0)
So(panel.Get("datasource").MustString(), ShouldEqual, "graphite")
}) })
}) })
......
...@@ -69,7 +69,7 @@ func SaveDashboard(cmd *m.SaveDashboardCommand) error { ...@@ -69,7 +69,7 @@ func SaveDashboard(cmd *m.SaveDashboardCommand) error {
affectedRows, err = sess.Insert(dash) affectedRows, err = sess.Insert(dash)
} else { } else {
dash.Version += 1 dash.Version += 1
dash.Data["version"] = dash.Version dash.Data.Set("version", dash.Version)
affectedRows, err = sess.Id(dash.Id).Update(dash) affectedRows, err = sess.Id(dash.Id).Update(dash)
} }
...@@ -108,7 +108,7 @@ func GetDashboard(query *m.GetDashboardQuery) error { ...@@ -108,7 +108,7 @@ func GetDashboard(query *m.GetDashboardQuery) error {
return m.ErrDashboardNotFound return m.ErrDashboardNotFound
} }
dashboard.Data["id"] = dashboard.Id dashboard.Data.Set("id", dashboard.Id)
query.Result = &dashboard query.Result = &dashboard
return nil return nil
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
"github.com/grafana/grafana/pkg/components/simplejson"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
) )
...@@ -16,9 +17,9 @@ func TestDashboardSnapshotDBAccess(t *testing.T) { ...@@ -16,9 +17,9 @@ func TestDashboardSnapshotDBAccess(t *testing.T) {
Convey("Given saved snaphot", func() { Convey("Given saved snaphot", func() {
cmd := m.CreateDashboardSnapshotCommand{ cmd := m.CreateDashboardSnapshotCommand{
Key: "hej", Key: "hej",
Dashboard: map[string]interface{}{ Dashboard: simplejson.NewFromAny(map[string]interface{}{
"hello": "mupp", "hello": "mupp",
}, }),
} }
err := CreateDashboardSnapshot(&cmd) err := CreateDashboardSnapshot(&cmd)
So(err, ShouldBeNil) So(err, ShouldBeNil)
...@@ -29,7 +30,7 @@ func TestDashboardSnapshotDBAccess(t *testing.T) { ...@@ -29,7 +30,7 @@ func TestDashboardSnapshotDBAccess(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(query.Result, ShouldNotBeNil) So(query.Result, ShouldNotBeNil)
So(query.Result.Dashboard["hello"], ShouldEqual, "mupp") So(query.Result.Dashboard.Get("hello").MustString(), ShouldEqual, "mupp")
}) })
}) })
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
"github.com/grafana/grafana/pkg/components/simplejson"
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/services/search"
) )
...@@ -12,11 +13,11 @@ import ( ...@@ -12,11 +13,11 @@ import (
func insertTestDashboard(title string, orgId int64, tags ...interface{}) *m.Dashboard { func insertTestDashboard(title string, orgId int64, tags ...interface{}) *m.Dashboard {
cmd := m.SaveDashboardCommand{ cmd := m.SaveDashboardCommand{
OrgId: orgId, OrgId: orgId,
Dashboard: map[string]interface{}{ Dashboard: simplejson.NewFromAny(map[string]interface{}{
"id": nil, "id": nil,
"title": title, "title": title,
"tags": tags, "tags": tags,
}, }),
} }
err := SaveDashboard(&cmd) err := SaveDashboard(&cmd)
...@@ -58,11 +59,11 @@ func TestDashboardDataAccess(t *testing.T) { ...@@ -58,11 +59,11 @@ func TestDashboardDataAccess(t *testing.T) {
cmd := m.SaveDashboardCommand{ cmd := m.SaveDashboardCommand{
OrgId: 1, OrgId: 1,
Overwrite: true, Overwrite: true,
Dashboard: map[string]interface{}{ Dashboard: simplejson.NewFromAny(map[string]interface{}{
"id": float64(123412321), "id": float64(123412321),
"title": "Expect error", "title": "Expect error",
"tags": []interface{}{}, "tags": []interface{}{},
}, }),
} }
err := SaveDashboard(&cmd) err := SaveDashboard(&cmd)
...@@ -76,11 +77,11 @@ func TestDashboardDataAccess(t *testing.T) { ...@@ -76,11 +77,11 @@ func TestDashboardDataAccess(t *testing.T) {
cmd := m.SaveDashboardCommand{ cmd := m.SaveDashboardCommand{
OrgId: 2, OrgId: 2,
Overwrite: true, Overwrite: true,
Dashboard: map[string]interface{}{ Dashboard: simplejson.NewFromAny(map[string]interface{}{
"id": float64(query.Result.Id), "id": float64(query.Result.Id),
"title": "Expect error", "title": "Expect error",
"tags": []interface{}{}, "tags": []interface{}{},
}, }),
} }
err := SaveDashboard(&cmd) err := SaveDashboard(&cmd)
...@@ -135,11 +136,11 @@ func TestDashboardDataAccess(t *testing.T) { ...@@ -135,11 +136,11 @@ func TestDashboardDataAccess(t *testing.T) {
Convey("Should not be able to save dashboard with same name", func() { Convey("Should not be able to save dashboard with same name", func() {
cmd := m.SaveDashboardCommand{ cmd := m.SaveDashboardCommand{
OrgId: 1, OrgId: 1,
Dashboard: map[string]interface{}{ Dashboard: simplejson.NewFromAny(map[string]interface{}{
"id": nil, "id": nil,
"title": "test dash 23", "title": "test dash 23",
"tags": []interface{}{}, "tags": []interface{}{},
}, }),
} }
err := SaveDashboard(&cmd) err := SaveDashboard(&cmd)
......
...@@ -137,8 +137,8 @@ export class DashNavCtrl { ...@@ -137,8 +137,8 @@ export class DashNavCtrl {
$scope.deleteDashboard = function() { $scope.deleteDashboard = function() {
$scope.appEvent('confirm-modal', { $scope.appEvent('confirm-modal', {
title: 'Delete dashboard', title: 'Delete',
text: 'Do you want to delete dashboard?', text: 'Do you want to delete this dashboard?',
text2: $scope.dashboard.title, text2: $scope.dashboard.title,
icon: 'fa-trash', icon: 'fa-trash',
yesText: 'Delete', yesText: 'Delete',
......
...@@ -21,13 +21,13 @@ ...@@ -21,13 +21,13 @@
</td> </td>
<td style="text-align: right"> <td style="text-align: right">
<button class="btn btn-secondary" ng-click="ctrl.import(dash, false)" ng-show="!dash.installed"> <button class="btn btn-secondary" ng-click="ctrl.import(dash, false)" ng-show="!dash.installed">
Install Import
</button> </button>
<button class="btn btn-secondary" ng-click="ctrl.import(dash, true)" ng-show="dash.installed"> <button class="btn btn-secondary" ng-click="ctrl.import(dash, true)" ng-show="dash.installed">
Re-Install Re-Import
</button> </button>
<button class="btn btn-danger" ng-click="ctrl.remove(dash)" ng-show="dash.installed"> <button class="btn btn-danger" ng-click="ctrl.remove(dash)" ng-show="dash.installed">
Un-install Delete
</button> </button>
</td> </td>
</tr> </tr>
......
...@@ -34,7 +34,7 @@ export class DashImportListCtrl { ...@@ -34,7 +34,7 @@ export class DashImportListCtrl {
}); });
} }
this.backendSrv.post(`/api/plugins/dashboards/install`, installCmd).then(res => { this.backendSrv.post(`/api/plugins/dashboards/import`, installCmd).then(res => {
this.$rootScope.appEvent('alert-success', ['Dashboard Installed', dash.title]); this.$rootScope.appEvent('alert-success', ['Dashboard Installed', dash.title]);
_.extend(dash, res); _.extend(dash, res);
}); });
......
...@@ -74,7 +74,7 @@ ...@@ -74,7 +74,7 @@
</div> </div>
<div ng-if="ctrl.tabIndex === 1" class="tab-content"> <div ng-if="ctrl.tabIndex === 1" class="tab-content">
<dashboard-import-list plugin="ctrl.datasourceMeta" datasource="ctrl.current" ></dashboard-import-list> <dashboard-import-list plugin="ctrl.datasourceMeta" datasource="ctrl.current"></dashboard-import-list>
</div> </div>
</div> </div>
......
{
"__inputs": {
"graphite": {
"type": "datasource",
"pluginId": "graphite",
"description": "Graphite datasource"
}
},
"revision": "1.0",
"title": "Graphite Carbon Metrics",
"tags": ["graphite", "carbon"],
"style": "dark",
"timezone": "browser",
"editable": true,
"hideControls": false,
"sharedCrosshair": false,
"rows": [
{
"collapsable": true,
"collapse": false,
"editable": true,
"height": "350px",
"notice": false,
"panels": [
{
"aliasColors": {},
"annotate": {
"enable": false
},
"bars": false,
"datasource": "$__graphite",
"editable": true,
"fill": 0,
"grid": {
"leftLogBase": 1,
"leftMax": null,
"leftMin": null,
"max": null,
"min": 0,
"rightLogBase": 1,
"rightMax": null,
"rightMin": null,
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 1,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"loadingEditor": false,
"nullPointMode": "null as zero",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"resolution": 100,
"scale": 1,
"seriesOverrides": [
{
"alias": "Points Per Update",
"yaxis": 2
},
{
"alias": "CPU",
"yaxis": 2
}
],
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"refId": "A",
"target": "alias(sumSeries(carbon.agents.*.updateOperations),\"Updates\") "
},
{
"refId": "B",
"target": "alias(sumSeries(carbon.agents.*.metricsReceived),'Metrics Received')"
},
{
"refId": "C",
"target": "alias(sumSeries(carbon.agents.*.committedPoints),'Committed Points')"
},
{
"refId": "D",
"target": "alias(sumSeries(carbon.agents.*.pointsPerUpdate),'Points Per Update')"
},
{
"refId": "E",
"target": "alias(averageSeries(carbon.agents.*.cpuUsage),'CPU')"
},
{
"refId": "F",
"target": "alias(sumSeries(carbon.agents.*.creates),'Creates')"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Graphite Carbon Metrics",
"tooltip": {
"query_as_alias": true,
"shared": false,
"value_type": "cumulative"
},
"type": "graph",
"x-axis": true,
"y-axis": true,
"y_formats": [
"short",
"short"
],
"zerofill": true
}
],
"title": "Row1"
}
],
"time": {
"from": "now-3h",
"to": "now"
},
"timepicker": {
"collapse": false,
"enable": true,
"notice": false,
"now": true,
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"status": "Stable",
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
],
"type": "timepicker"
},
"templating": {
"enable": false,
"list": []
},
"annotations": {
"enable": false,
"list": []
},
"refresh": false,
"schemaVersion": 8,
"version": 2,
"links": []
}
{
"__inputs": {
"graphite": {
"type": "datasource",
"pluginId": "graphite",
"description": "Graphite datasource"
}
},
"title": "Carbon Cache Stats",
"version": 1,
"rows": [
{
"panels": [
{
"type": "graph",
"datasource": "$__graphite"
}
]
}
]
}
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
"id": "graphite", "id": "graphite",
"includes": [ "includes": [
{"type": "dashboard", "name": "Carbon Cache Stats", "path": "dashboards/carbon_stats.json"} {"type": "dashboard", "name": "Graphite Carbon Metrics", "path": "dashboards/carbon_metrics.json"}
], ],
"metrics": true, "metrics": true,
......
...@@ -10,6 +10,10 @@ ...@@ -10,6 +10,10 @@
"title": "Nginx Connections", "title": "Nginx Connections",
"revision": "1.5", "revision": "1.5",
"schemaVersion": 11, "schemaVersion": 11,
"tags": ["tag1", "tag2"],
"number_array": [1,2,3,10.33],
"boolean_true": true,
"boolean_false": false,
"rows": [ "rows": [
{ {
......
{
"revision": "1.5",
"tags": ["tag1", "tag2"],
"boolean_false": false,
"boolean_true": true,
"number_array": [1,2,3,10.33],
"rows": [
{
"panels": [
{
"datasource": "graphite",
"type": "graph"
}
]
}
],
"schemaVersion": 11,
"title": "Nginx Connections",
"version": 0
}
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