Commit c3708b30 by Torkel Ödegaard

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

parent 22a7eaf2
......@@ -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")
......
......@@ -17,7 +17,7 @@ define([
'./importCtrl',
'./impression_store',
'./upload',
'./import/import',
'./import/dash_import',
'./export/export_modal',
'./dash_list_ctrl',
], function () {});
......@@ -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
......
......@@ -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>
......
......@@ -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() {
......
......@@ -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">
......
......@@ -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');
});
});
});
......@@ -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 {
......
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