瀏覽代碼

feat(import): import directly from grafana.net id/url now works

Torkel Ödegaard 9 年之前
父節點
當前提交
c3708b3096

+ 9 - 4
pkg/api/gnetproxy.go

@@ -5,8 +5,10 @@ import (
 	"net"
 	"net/http"
 	"net/http/httputil"
+	"net/url"
 	"time"
 
+	"github.com/Unknwon/log"
 	"github.com/grafana/grafana/pkg/middleware"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/util"
@@ -23,12 +25,15 @@ var gNetProxyTransport = &http.Transport{
 }
 
 func ReverseProxyGnetReq(proxyPath string) *httputil.ReverseProxy {
+	url, _ := url.Parse(setting.GrafanaNetUrl)
+
 	director := func(req *http.Request) {
-		req.URL.Scheme = "https"
-		req.URL.Host = "grafana.net"
-		req.Host = "grafana.net"
+		req.URL.Scheme = url.Scheme
+		req.URL.Host = url.Host
+		req.Host = url.Host
 
-		req.URL.Path = util.JoinUrlFragments(setting.GrafanaNetUrl+"/api", proxyPath)
+		req.URL.Path = util.JoinUrlFragments(url.Path+"/api", proxyPath)
+		log.Info("Url: %v", req.URL.Path)
 
 		// clear cookie headers
 		req.Header.Del("Cookie")

+ 1 - 1
public/app/features/dashboard/all.js

@@ -17,7 +17,7 @@ define([
   './importCtrl',
   './impression_store',
   './upload',
-  './import/import',
+  './import/dash_import',
   './export/export_modal',
   './dash_list_ctrl',
 ], function () {});

+ 0 - 10
public/app/features/dashboard/export/export_modal.html

@@ -15,16 +15,6 @@
 			You can share dashboards on <a class="external-link" href="https://grafana.net">Grafana.net</a>
 		</p>
 
-		<div class="gf-form-group">
-			<div class="gf-form">
-				<label class="gf-form-label width-8">Title</label>
-				<input type="text" class="gf-form-input" ng-model="ctrl.dash.title" ng-change="ctrl.titleChanged()">
-				<label class="gf-form-label text-success" ng-show="ctrl.dash.title">
-					<i class="fa fa-check"></i>
-				</label>
-			</div>
-		</div>
-
 		<div class="gf-form-button-row">
 			<button type="button" class="btn gf-form-btn width-10 btn-success" ng-click="ctrl.save()">
 				<i class="fa fa-save"></i> Save to file

+ 31 - 15
public/app/features/dashboard/import/dash_import.html

@@ -22,7 +22,7 @@
 
       <div class="gf-form-group">
 				<div class="gf-form">
-					<input type="text" class="gf-form-input" ng-ctrl="ctrl.grafanaNetUrl" placeholder="Paste Grafana.net dashboard url or id" ng-change="ctrl.checkGnetDashboard()"></textarea>
+					<input type="text" class="gf-form-input" ng-model="ctrl.gnetUrl" placeholder="Paste Grafana.net dashboard url or id" ng-blur="ctrl.checkGnetDashboard()"></textarea>
 				</div>
         <div class="gf-form" ng-if="ctrl.gnetError">
           <label class="gf-form-label text-warning">
@@ -50,6 +50,21 @@
 		</div>
 
 		<div ng-if="ctrl.step === 2">
+      <h3 class="section-heading" ng-if="ctrl.dash.gnetId">
+        Importing Dashboard from
+        <a href="https://grafana.net/dashboards/{{ctrl.dash.gnetId}}" class="external-link" target="_blank">Grafana.net</a>
+			</h3>
+			<div class="gf-form-group">
+        <div class="gf-form">
+          <label class="gf-form-label width-15">Published by</label>
+          <label class="gf-form-label width-15">{{ctrl.gnetInfo.orgName}}</label>
+        </div>
+        <div class="gf-form">
+          <label class="gf-form-label width-15">Updated on</label>
+          <label class="gf-form-label width-15">{{ctrl.gnetInfo.updatedAt | date : 'yyyy-MM-dd HH:mm:ss'}}</label>
+        </div>
+      </div>
+
 			<h3 class="section-heading">
 				Options
 			</h3>
@@ -57,7 +72,7 @@
 			<div class="gf-form-group">
 				<div class="gf-form-inline">
 					<div class="gf-form gf-form--grow">
-						<label class="gf-form-label width-15">Title</label>
+						<label class="gf-form-label width-15">Name</label>
 						<input type="text" class="gf-form-input" ng-model="ctrl.dash.title" give-focus="true" ng-change="ctrl.titleChanged()" ng-class="{'validation-error': ctrl.nameExists}">
 						<label class="gf-form-label text-success" ng-if="!ctrl.nameExists">
 							<i class="fa fa-check"></i>
@@ -76,32 +91,33 @@
 
 				<div ng-repeat="input in ctrl.inputs">
 					<div class="gf-form">
-						<label class="gf-form-label width-15">{{input.label}}</label>
+						<label class="gf-form-label width-15">
+              {{input.label}}
+              <info-popover mode="right-normal">
+                {{input.info}}
+              </info-popover>
+            </label>
 						<div class="gf-form-select-wrapper" style="width: 100%">
-							<select class="gf-form-input" ng-model="input.value" ng-options="v.value as v.text for v in input.options" ng-change="ctrl.inputValueChanged()"></select>
+							<select class="gf-form-input" ng-model="input.value" ng-options="v.value as v.text for v in input.options" ng-change="ctrl.inputValueChanged()">
+                <option value="" ng-hide="input.value">{{input.info}}</option>
+              </select>
 						</div>
 						<label class="gf-form-label text-success" ng-show="input.value">
 							<i class="fa fa-check"></i>
 						</label>
 					</div>
-					<div class="gf-form offset-width-15 gf-form--grow">
-						<label class="gf-form-label gf-form-label--grow" ng-show="input.info">
-							<i class="fa fa-info-circle"></i>
-							{{input.info}}
-						</label>
-						<label class="gf-form-label gf-form-label--grow" ng-show="input.error">
-							<i class="fa fa-info-circle"></i>
-							{{input.info}}
-						</label>
-					</div>
 				</div>
 			</div>
 
 			<div class="gf-form-button-row">
-				<button type="button" class="btn gf-form-btn width-10" ng-click="ctrl.saveDashboard()" ng-class="{'btn-danger': ctrl.nameExists, 'btn-success': !ctrl.nameExists}" ng-disabled="!ctrl.inputsValid">
+				<button type="button" class="btn gf-form-btn btn-success width-10" ng-click="ctrl.saveDashboard()" ng-hide="ctrl.nameExists" ng-disabled="!ctrl.inputsValid">
 					<i class="fa fa-save"></i> Save &amp; Open
 				</button>
+        <button type="button" class="btn gf-form-btn btn-danger width-10" ng-click="ctrl.saveDashboard()" ng-show="ctrl.nameExists" ng-disabled="!ctrl.inputsValid">
+					<i class="fa fa-save"></i> Overwrite &amp; Open
+				</button>
 				<a class="btn btn-link" ng-click="dismiss()">Cancel</a>
+				<a class="btn btn-link" ng-click="ctrl.back()">Back</a>
 			</div>
 
 		</div>

+ 41 - 1
public/app/features/dashboard/import/dash_import.ts

@@ -14,11 +14,20 @@ export class DashImportCtrl {
   dash: any;
   inputs: any[];
   inputsValid: boolean;
+  gnetUrl: string;
+  gnetError: string;
+  gnetInfo: any;
 
   /** @ngInject */
-  constructor(private backendSrv, private $location, private $scope) {
+  constructor(private backendSrv, private $location, private $scope, private $routeParams) {
     this.step = 1;
     this.nameExists = false;
+
+    // check gnetId in url
+    if ($routeParams.gnetId)  {
+      this.gnetUrl = $routeParams.gnetId ;
+      this.checkGnetDashboard();
+    }
   }
 
   onUpload(dash) {
@@ -121,6 +130,37 @@ export class DashImportCtrl {
     }
   }
 
+  checkGnetDashboard() {
+    this.gnetError = '';
+
+    var match = /(^\d+$)|dashboards\/(\d+)/.exec(this.gnetUrl);
+    var dashboardId;
+
+    if (match && match[1]) {
+      dashboardId = match[1];
+    } else if (match && match[2]) {
+      dashboardId = match[2];
+    } else {
+      this.gnetError = 'Could not find dashboard';
+    }
+
+    return this.backendSrv.get('api/gnet/dashboards/' + dashboardId).then(res => {
+      this.gnetInfo = res;
+      // store reference to grafana.net
+      res.json.gnetId = dashboardId;
+      this.onUpload(res.json);
+    }).catch(err => {
+      this.gnetError = err.message || err;
+    });
+  }
+
+  back() {
+    this.gnetUrl = '';
+    this.step = 1;
+    this.gnetError = '';
+    this.gnetInfo = '';
+  }
+
 }
 
 export function dashImportDirective() {

+ 1 - 1
public/app/features/dashboard/partials/settings.html

@@ -22,7 +22,7 @@
 		<div class="gf-form-group section">
       <h5 class="section-heading">Details</h5>
 			<div class="gf-form">
-				<label class="gf-form-label width-7">Title</label>
+				<label class="gf-form-label width-7">Name</label>
 				<input type="text" class="gf-form-input width-30" ng-model='dashboard.title'></input>
 			</div>
       <div class="gf-form">

+ 36 - 2
public/app/features/dashboard/specs/dash_import_ctrl_specs.ts

@@ -7,6 +7,7 @@ describe('DashImportCtrl', function() {
   var ctx: any = {};
   var backendSrv = {
     search: sinon.stub().returns(Promise.resolve([])),
+    get: sinon.stub()
   };
 
   beforeEach(angularMocks.module('grafana.core'));
@@ -20,7 +21,7 @@ describe('DashImportCtrl', function() {
     });
   }));
 
-  describe('when upload json', function() {
+  describe('when uploading json', function() {
     beforeEach(function() {
       config.datasources = {
         ds: {
@@ -37,14 +38,47 @@ describe('DashImportCtrl', function() {
 
     it('should build input model', function() {
       expect(ctx.ctrl.inputs.length).to.eql(1);
-      expect(ctx.ctrl.inputs[0].label).to.eql(1);
+      expect(ctx.ctrl.inputs[0].name).to.eql('ds');
+      expect(ctx.ctrl.inputs[0].info).to.eql('Select a Test DB data source');
     });
 
     it('should set inputValid to false', function() {
       expect(ctx.ctrl.inputsValid).to.eql(false);
     });
+  });
+
+  describe('when specifing grafana.net url', function() {
+    beforeEach(function() {
+      ctx.ctrl.gnetUrl = 'http://grafana.net/dashboards/123';
+      // setup api mock
+      backendSrv.get = sinon.spy(() => {
+        return Promise.resolve({
+        });
+      });
+      ctx.ctrl.checkGnetDashboard();
+    });
+
+    it('should call gnet api with correct dashboard id', function() {
+      expect(backendSrv.get.getCall(0).args[0]).to.eql('api/gnet/dashboards/123');
+    });
+  });
 
+  describe('when specifing dashbord id', function() {
+    beforeEach(function() {
+      ctx.ctrl.gnetUrl = '2342';
+      // setup api mock
+      backendSrv.get = sinon.spy(() => {
+        return Promise.resolve({
+        });
+      });
+      ctx.ctrl.checkGnetDashboard();
+    });
+
+    it('should call gnet api with correct dashboard id', function() {
+      expect(backendSrv.get.getCall(0).args[0]).to.eql('api/gnet/dashboards/2342');
+    });
   });
+
 });
 
 

+ 4 - 0
public/sass/components/_gf-form.scss

@@ -158,6 +158,10 @@ $gf-form-margin: 0.25rem;
       color: transparent;
       text-shadow: 0 0 0 $text-color;
     }
+
+    &.ng-empty {
+      color: $text-color-weak;
+    }
   }
 
   &:after {