mirror of
https://github.com/grafana/grafana.git
synced 2024-12-28 18:01:40 -06:00
feat(plugins): dashboard import for data sources is working! #4298
This commit is contained in:
parent
3fb0b71822
commit
0398face05
@ -53,7 +53,6 @@ func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapsho
|
||||
}
|
||||
|
||||
func GetDashboardSnapshot(c *middleware.Context) {
|
||||
|
||||
key := c.Params(":key")
|
||||
query := &m.GetDashboardSnapshotQuery{Key: key}
|
||||
|
||||
@ -136,5 +135,4 @@ func SearchDashboardSnapshots(c *middleware.Context) Response {
|
||||
}
|
||||
|
||||
return Json(200, dtos)
|
||||
//return Json(200, searchQuery.Result)
|
||||
}
|
||||
|
@ -53,8 +53,8 @@ type DashboardMeta struct {
|
||||
}
|
||||
|
||||
type DashboardFullWithMeta struct {
|
||||
Meta DashboardMeta `json:"meta"`
|
||||
Dashboard map[string]interface{} `json:"dashboard"`
|
||||
Meta DashboardMeta `json:"meta"`
|
||||
Dashboard *simplejson.Json `json:"dashboard"`
|
||||
}
|
||||
|
||||
type DataSource struct {
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
@ -14,7 +13,6 @@ import (
|
||||
|
||||
// Implements the json.Unmarshaler interface.
|
||||
func (j *Json) UnmarshalJSON(p []byte) error {
|
||||
fmt.Printf("UnmarshalJSON")
|
||||
dec := json.NewDecoder(bytes.NewBuffer(p))
|
||||
dec.UseNumber()
|
||||
return dec.Decode(&j.data)
|
||||
|
@ -1,6 +1,10 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
)
|
||||
|
||||
// DashboardSnapshot model
|
||||
type DashboardSnapshot struct {
|
||||
@ -17,7 +21,7 @@ type DashboardSnapshot struct {
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
|
||||
Dashboard map[string]interface{}
|
||||
Dashboard *simplejson.Json
|
||||
}
|
||||
|
||||
// DashboardSnapshotDTO without dashboard map
|
||||
@ -40,9 +44,9 @@ type DashboardSnapshotDTO struct {
|
||||
// COMMANDS
|
||||
|
||||
type CreateDashboardSnapshotCommand struct {
|
||||
Dashboard map[string]interface{} `json:"dashboard" binding:"Required"`
|
||||
Name string `json:"name" binding:"Required"`
|
||||
Expires int64 `json:"expires"`
|
||||
Dashboard *simplejson.Json `json:"dashboard" binding:"Required"`
|
||||
Name string `json:"name" binding:"Required"`
|
||||
Expires int64 `json:"expires"`
|
||||
|
||||
// these are passed when storing an external snapshot ref
|
||||
External bool `json:"external"`
|
||||
|
@ -1,8 +1,8 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
@ -127,32 +127,45 @@ func (this *DashTemplateEvaluator) Eval() (*simplejson.Json, error) {
|
||||
log.Info("Import: dashboard has no __import section")
|
||||
}
|
||||
|
||||
this.EvalObject(this.template, this.result)
|
||||
return this.result, nil
|
||||
return simplejson.NewFromAny(this.evalObject(this.template)), nil
|
||||
}
|
||||
|
||||
func (this *DashTemplateEvaluator) EvalObject(source *simplejson.Json, writer *simplejson.Json) {
|
||||
func (this *DashTemplateEvaluator) evalValue(source *simplejson.Json) interface{} {
|
||||
|
||||
sourceValue := source.Interface()
|
||||
|
||||
switch v := sourceValue.(type) {
|
||||
case string:
|
||||
interpolated := this.varRegex.ReplaceAllStringFunc(v, func(match string) string {
|
||||
return this.variables[match]
|
||||
})
|
||||
return interpolated
|
||||
case bool:
|
||||
return v
|
||||
case json.Number:
|
||||
return v
|
||||
case map[string]interface{}:
|
||||
return this.evalObject(source)
|
||||
case []interface{}:
|
||||
array := make([]interface{}, 0)
|
||||
for _, item := range v {
|
||||
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
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
interpolated := this.varRegex.ReplaceAllStringFunc(v, func(match string) string {
|
||||
return this.variables[match]
|
||||
})
|
||||
writer.Set(key, interpolated)
|
||||
case map[string]interface{}:
|
||||
childSource := simplejson.NewFromAny(value)
|
||||
childWriter := simplejson.New()
|
||||
writer.Set(key, childWriter.Interface())
|
||||
this.EvalObject(childSource, childWriter)
|
||||
case []interface{}:
|
||||
default:
|
||||
log.Info("type: %v", reflect.TypeOf(value))
|
||||
log.Error(3, "Unknown json type key: %v , type: %v", key, value)
|
||||
}
|
||||
result[key] = this.evalValue(simplejson.NewFromAny(value))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
@ -34,7 +35,7 @@ func TestDashboardImport(t *testing.T) {
|
||||
OrgId: 1,
|
||||
UserId: 1,
|
||||
Inputs: []ImportDashboardInput{
|
||||
{Name: "*", Type: "datasource"},
|
||||
{Name: "*", Type: "datasource", Value: "graphite"},
|
||||
},
|
||||
}
|
||||
|
||||
@ -44,11 +45,15 @@ func TestDashboardImport(t *testing.T) {
|
||||
Convey("should install dashboard", func() {
|
||||
So(importedDash, ShouldNotBeNil)
|
||||
|
||||
dashStr, _ := importedDash.Data.EncodePretty()
|
||||
So(string(dashStr), ShouldEqual, "")
|
||||
resultStr, _ := importedDash.Data.EncodePretty()
|
||||
expectedBytes, _ := ioutil.ReadFile("../../tests/test-app/dashboards/connections_result.json")
|
||||
expectedJson, _ := simplejson.NewJson(expectedBytes)
|
||||
expectedStr, _ := expectedJson.EncodePretty()
|
||||
|
||||
// So(panel["datasource"], ShouldEqual, "graphite")
|
||||
// So(importedDash.Data["__inputs"], ShouldBeNil)
|
||||
So(string(resultStr), ShouldEqual, string(expectedStr))
|
||||
|
||||
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 {
|
||||
affectedRows, err = sess.Insert(dash)
|
||||
} else {
|
||||
dash.Version += 1
|
||||
dash.Data["version"] = dash.Version
|
||||
dash.Data.Set("version", dash.Version)
|
||||
affectedRows, err = sess.Id(dash.Id).Update(dash)
|
||||
}
|
||||
|
||||
@ -108,7 +108,7 @@ func GetDashboard(query *m.GetDashboardQuery) error {
|
||||
return m.ErrDashboardNotFound
|
||||
}
|
||||
|
||||
dashboard.Data["id"] = dashboard.Id
|
||||
dashboard.Data.Set("id", dashboard.Id)
|
||||
query.Result = &dashboard
|
||||
|
||||
return nil
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
@ -16,9 +17,9 @@ func TestDashboardSnapshotDBAccess(t *testing.T) {
|
||||
Convey("Given saved snaphot", func() {
|
||||
cmd := m.CreateDashboardSnapshotCommand{
|
||||
Key: "hej",
|
||||
Dashboard: map[string]interface{}{
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"hello": "mupp",
|
||||
},
|
||||
}),
|
||||
}
|
||||
err := CreateDashboardSnapshot(&cmd)
|
||||
So(err, ShouldBeNil)
|
||||
@ -29,7 +30,7 @@ func TestDashboardSnapshotDBAccess(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
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 (
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/search"
|
||||
)
|
||||
@ -12,11 +13,11 @@ import (
|
||||
func insertTestDashboard(title string, orgId int64, tags ...interface{}) *m.Dashboard {
|
||||
cmd := m.SaveDashboardCommand{
|
||||
OrgId: orgId,
|
||||
Dashboard: map[string]interface{}{
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": title,
|
||||
"tags": tags,
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
err := SaveDashboard(&cmd)
|
||||
@ -58,11 +59,11 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
cmd := m.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
Overwrite: true,
|
||||
Dashboard: map[string]interface{}{
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": float64(123412321),
|
||||
"title": "Expect error",
|
||||
"tags": []interface{}{},
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
err := SaveDashboard(&cmd)
|
||||
@ -76,11 +77,11 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
cmd := m.SaveDashboardCommand{
|
||||
OrgId: 2,
|
||||
Overwrite: true,
|
||||
Dashboard: map[string]interface{}{
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": float64(query.Result.Id),
|
||||
"title": "Expect error",
|
||||
"tags": []interface{}{},
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
err := SaveDashboard(&cmd)
|
||||
@ -135,11 +136,11 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
Convey("Should not be able to save dashboard with same name", func() {
|
||||
cmd := m.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
Dashboard: map[string]interface{}{
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": "test dash 23",
|
||||
"tags": []interface{}{},
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
err := SaveDashboard(&cmd)
|
||||
|
@ -137,8 +137,8 @@ export class DashNavCtrl {
|
||||
|
||||
$scope.deleteDashboard = function() {
|
||||
$scope.appEvent('confirm-modal', {
|
||||
title: 'Delete dashboard',
|
||||
text: 'Do you want to delete dashboard?',
|
||||
title: 'Delete',
|
||||
text: 'Do you want to delete this dashboard?',
|
||||
text2: $scope.dashboard.title,
|
||||
icon: 'fa-trash',
|
||||
yesText: 'Delete',
|
||||
|
@ -21,13 +21,13 @@
|
||||
</td>
|
||||
<td style="text-align: right">
|
||||
<button class="btn btn-secondary" ng-click="ctrl.import(dash, false)" ng-show="!dash.installed">
|
||||
Install
|
||||
Import
|
||||
</button>
|
||||
<button class="btn btn-secondary" ng-click="ctrl.import(dash, true)" ng-show="dash.installed">
|
||||
Re-Install
|
||||
Re-Import
|
||||
</button>
|
||||
<button class="btn btn-danger" ng-click="ctrl.remove(dash)" ng-show="dash.installed">
|
||||
Un-install
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -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]);
|
||||
_.extend(dash, res);
|
||||
});
|
||||
|
@ -74,7 +74,7 @@
|
||||
</div>
|
||||
|
||||
<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>
|
||||
|
@ -0,0 +1,176 @@
|
||||
{
|
||||
"__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": []
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
{
|
||||
"__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 @@
|
||||
"id": "graphite",
|
||||
|
||||
"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,
|
||||
|
@ -10,6 +10,10 @@
|
||||
"title": "Nginx Connections",
|
||||
"revision": "1.5",
|
||||
"schemaVersion": 11,
|
||||
"tags": ["tag1", "tag2"],
|
||||
"number_array": [1,2,3,10.33],
|
||||
"boolean_true": true,
|
||||
"boolean_false": false,
|
||||
|
||||
"rows": [
|
||||
{
|
||||
|
20
tests/test-app/dashboards/connections_result.json
Normal file
20
tests/test-app/dashboards/connections_result.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"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
|
||||
}
|
Loading…
Reference in New Issue
Block a user