Browse Source

Began work on data source test / validation, #1997 & #2043

Torkel Ödegaard 10 years ago
parent
commit
e2f6633d57

+ 1 - 0
CHANGELOG.md

@@ -21,6 +21,7 @@
 - [Issue #1928](https://github.com/grafana/grafana/issues/1928). HTTP API: GET /api/dashboards/db/:slug response changed property `model` to `dashboard` to match the POST request nameing
 - Backend render URL changed from `/render/dashboard/solo` `render/dashboard-solo/` (in order to have consistent dashboard url `/dashboard/:type/:slug`)
 - Search HTTP API response has changed (simplified), tags list moved to seperate HTTP resource URI
+- Datasource HTTP api breaking change, ADD datasource is now POST /api/datasources/, update is now PUT /api/datasources/:id
 
 # 2.0.3 (unreleased - 2.0.x branch)
 

+ 3 - 4
pkg/api/api.go

@@ -107,10 +107,9 @@ func Register(r *macaron.Macaron) {
 
 		// Data sources
 		r.Group("/datasources", func() {
-			r.Combo("/").
-				Get(GetDataSources).
-				Put(bind(m.AddDataSourceCommand{}), AddDataSource).
-				Post(bind(m.UpdateDataSourceCommand{}), UpdateDataSource)
+			r.Get("/", GetDataSources)
+			r.Post("/", bind(m.AddDataSourceCommand{}), AddDataSource)
+			r.Put("/:id", bind(m.UpdateDataSourceCommand{}), UpdateDataSource)
 			r.Delete("/:id", DeleteDataSource)
 			r.Get("/:id", GetDataSourceById)
 			r.Get("/plugins", GetDataSourcePlugins)

+ 3 - 1
pkg/api/datasources.go

@@ -6,6 +6,7 @@ import (
 	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/plugins"
+	"github.com/grafana/grafana/pkg/util"
 )
 
 func GetDataSources(c *middleware.Context) {
@@ -94,11 +95,12 @@ func AddDataSource(c *middleware.Context, cmd m.AddDataSourceCommand) {
 		return
 	}
 
-	c.JsonOK("Datasource added")
+	c.JSON(200, util.DynMap{"message": "Datasource added", "id": cmd.Result.Id})
 }
 
 func UpdateDataSource(c *middleware.Context, cmd m.UpdateDataSourceCommand) {
 	cmd.OrgId = c.OrgId
+	cmd.Id = c.ParamsInt64(":id")
 
 	err := bus.Dispatch(&cmd)
 	if err != nil {

+ 1 - 1
pkg/models/datasource.go

@@ -69,7 +69,6 @@ type AddDataSourceCommand struct {
 
 // Also acts as api DTO
 type UpdateDataSourceCommand struct {
-	Id                int64                  `json:"id" binding:"Required"`
 	Name              string                 `json:"name" binding:"Required"`
 	Type              string                 `json:"type" binding:"Required"`
 	Access            DsAccess               `json:"access" binding:"Required"`
@@ -84,6 +83,7 @@ type UpdateDataSourceCommand struct {
 	JsonData          map[string]interface{} `json:"jsonData"`
 
 	OrgId int64 `json:"-"`
+	Id    int64 `json:"-"`
 }
 
 type DeleteDataSourceCommand struct {

+ 33 - 13
public/app/features/org/datasourceEditCtrl.js

@@ -25,7 +25,6 @@ function (angular, config) {
 
       $scope.loadDatasourceTypes().then(function() {
         if ($routeParams.id) {
-          $scope.isNew = false;
           $scope.getDatasourceById($routeParams.id);
         } else {
           $scope.current = angular.copy(defaults);
@@ -48,6 +47,7 @@ function (angular, config) {
 
     $scope.getDatasourceById = function(id) {
       backendSrv.get('/api/datasources/' + id).then(function(ds) {
+        $scope.isNew = false;
         $scope.current = ds;
         $scope.typeChanged();
       });
@@ -65,26 +65,46 @@ function (angular, config) {
       });
     };
 
-    $scope.update = function() {
-      if (!$scope.editForm.$valid) {
-        return;
-      }
+    $scope.testDatasource = function() {
+      $scope.testing = { done: false };
 
-      backendSrv.post('/api/datasources', $scope.current).then(function() {
-        $scope.updateFrontendSettings();
-        $location.path("datasources");
+      datasourceSrv.get($scope.current.name).then(function(datasource) {
+        if (!datasource.testDatasource) {
+          $scope.testing.message = 'Data source does not support test connection feature.';
+          $scope.testing.status = 'warning';
+          $scope.testing.title = 'Unknown';
+          return;
+        }
+        return datasource.testDatasource().then(function(result) {
+          $scope.testing.message = result.message;
+          $scope.testing.status = result.status;
+          $scope.testing.title = result.title;
+        });
+      }).finally(function() {
+        $scope.testing.done = true;
       });
     };
 
-    $scope.add = function() {
+    $scope.saveChanges = function(test) {
       if (!$scope.editForm.$valid) {
         return;
       }
 
-      backendSrv.put('/api/datasources', $scope.current).then(function() {
-        $scope.updateFrontendSettings();
-        $location.path("datasources");
-      });
+      if ($scope.current.id) {
+        return backendSrv.put('/api/datasources/' + $scope.current.id, $scope.current).then(function() {
+          $scope.updateFrontendSettings();
+          if (test) {
+            $scope.testDatasource();
+          } else {
+            $location.path('datasources');
+          }
+        });
+      } else {
+        return backendSrv.post('/api/datasources', $scope.current).then(function(result) {
+          $scope.updateFrontendSettings();
+          $location.path('datasources/edit/' + result.id);
+        });
+      }
     };
 
     $scope.init();

+ 16 - 5
public/app/features/org/partials/datasourceEdit.html

@@ -43,11 +43,22 @@
 			</div>
 
 			<div ng-include="datasourceMeta.partials.config" ng-if="datasourceMeta.partials.config"></div>
-			<br>
-			<br>
-			<div class="pull-right">
-				<button type="submit" class="btn btn-success" ng-show="isNew" ng-click="add()">Add</button>
-				<button type="submit" class="btn btn-success" ng-show="!isNew" ng-click="update()">Update</button>
+
+			<div ng-if="testing" style="margin-top: 25px">
+				<h5 ng-show="!testing.done">Testing.... <i class="fa fa-spiner fa-spin"></i></h5>
+				<h5 ng-show="testing.done">Test results</h5>
+				<div class="alert-{{testing.status}} alert">
+					<div class="alert-title">{{testing.title}}</div>
+					<div ng-bind='testing.message'></div>
+				</div>
+			</div>
+
+			<div class="pull-right" style="margin-top: 35px">
+				<button type="submit" class="btn btn-success" ng-show="isNew" ng-click="saveChanges()">Add</button>
+				<button type="submit" class="btn btn-success" ng-show="!isNew" ng-click="saveChanges()">Save</button>
+				<button type="submit" class="btn btn-inverse" ng-show="!isNew" ng-click="saveChanges(true)">
+					Test Connection
+				</button>
 				<a class="btn btn-inverse" ng-show="!isNew" href="datasources">Cancel</a>
 			</div>
 			<br>

+ 16 - 0
public/app/plugins/datasource/graphite/datasource.js

@@ -196,6 +196,22 @@ function (angular, _, $, config, kbn, moment) {
         });
     };
 
+    GraphiteDatasource.prototype.testDatasource = function() {
+      return this.metricFindQuery('*').then(function () {
+        return { status: "success", message: "Data source is working", title: "Success" };
+      }, function(err) {
+        var message, title;
+        if (err.statusText) {
+          message = err.statusText;
+          title = "HTTP Error";
+        } else {
+          message = err;
+          title = "Unknown error";
+        }
+        return { status: "error", message: message, title: title };
+      });
+    };
+
     GraphiteDatasource.prototype.listDashboards = function(query) {
       return this.doGraphiteRequest({ method: 'GET',  url: '/dashboard/find/', params: {query: query || ''} })
         .then(function(results) {

+ 22 - 23
public/css/less/overrides.less

@@ -315,38 +315,37 @@ div.flot-text {
   position: fixed;
   right: 20px;
   top: 56px;
+}
 
-  .alert {
-    color: @white;
-    padding-bottom: 13px;
-    position: relative;
-  }
+.alert {
+  color: @white;
+  padding-bottom: 13px;
+  position: relative;
+}
 
-  .alert-close {
-    position: absolute;
-    top: -4px;
-    right: -2px;
-    width: 19px;
-    height: 19px;
-    padding: 0;
-    background: @grayLighter;
-    border-radius: 50%;
-    border: none;
-    font-size: 1.1rem;
-    color: @grayDarker;
-  }
+.alert-close {
+  position: absolute;
+  top: -4px;
+  right: -2px;
+  width: 19px;
+  height: 19px;
+  padding: 0;
+  background: @grayLighter;
+  border-radius: 50%;
+  border: none;
+  font-size: 1.1rem;
+  color: @grayDarker;
+}
 
-  .alert-title {
-    font-weight: bold;
-    padding-bottom: 2px;
-  }
+.alert-title {
+  font-weight: bold;
+  padding-bottom: 2px;
 }
 
 
 .alert-warning {
   background-color: @warningBackground;
   border-color: @warningBorder;
-  color: @warningText;
 }
 
 /* ===================================================