Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
cc2349f6
Commit
cc2349f6
authored
Dec 05, 2017
by
Torkel Ödegaard
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'feat-10064' of
https://github.com/alexanderzobnin/grafana
into develop
parents
31bb2f80
4d7ff4de
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
322 additions
and
30 deletions
+322
-30
pkg/api/index.go
+2
-1
public/app/core/routes/routes.ts
+5
-0
public/app/features/dashboard/all.ts
+2
-1
public/app/features/dashboard/dashboard_import_ctrl.ts
+163
-0
public/app/features/dashboard/partials/dashboardImport.html
+125
-0
public/app/features/dashboard/specs/dashboard_import_ctrl.jest.ts
+25
-28
No files found.
pkg/api/index.go
View file @
cc2349f6
...
...
@@ -90,12 +90,13 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
if
c
.
OrgRole
==
m
.
ROLE_ADMIN
||
c
.
OrgRole
==
m
.
ROLE_EDITOR
{
data
.
NavTree
=
append
(
data
.
NavTree
,
&
dtos
.
NavLink
{
Text
:
"Create"
,
Id
:
"create"
,
Icon
:
"fa fa-fw fa-plus"
,
Url
:
"#"
,
Children
:
[]
*
dtos
.
NavLink
{
{
Text
:
"Dashboard"
,
Icon
:
"gicon gicon-dashboard-new"
,
Url
:
setting
.
AppSubUrl
+
"/dashboard/new"
},
{
Text
:
"Folder"
,
Icon
:
"gicon gicon-folder-new"
,
Url
:
setting
.
AppSubUrl
+
"/dashboard/new/?editview=new-folder"
},
{
Text
:
"Import"
,
I
con
:
"gicon gicon-dashboard-import"
,
Url
:
setting
.
AppSubUrl
+
"/dashboard/new/?editview=
import"
},
{
Text
:
"Import"
,
I
d
:
"import"
,
Icon
:
"gicon gicon-dashboard-import"
,
Url
:
setting
.
AppSubUrl
+
"/dashboard/
import"
},
},
})
}
...
...
public/app/core/routes/routes.ts
View file @
cc2349f6
...
...
@@ -48,6 +48,11 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
reloadOnSearch
:
false
,
pageClass
:
'page-dashboard'
,
})
.
when
(
'/dashboard/import'
,
{
templateUrl
:
'public/app/features/dashboard/partials/dashboardImport.html'
,
controller
:
'DashboardImportCtrl'
,
controllerAs
:
'ctrl'
,
})
.
when
(
'/datasources'
,
{
templateUrl
:
'public/app/features/plugins/partials/ds_list.html'
,
controller
:
'DataSourcesCtrl'
,
...
...
public/app/features/dashboard/all.ts
View file @
cc2349f6
...
...
@@ -15,7 +15,6 @@ import './unsavedChangesSrv';
import
'./unsaved_changes_modal'
;
import
'./timepicker/timepicker'
;
import
'./upload'
;
import
'./import/dash_import'
;
import
'./export/export_modal'
;
import
'./export_data/export_data_modal'
;
import
'./ad_hoc_filters'
;
...
...
@@ -30,5 +29,7 @@ import './move_to_folder_modal/move_to_folder';
import
coreModule
from
'app/core/core_module'
;
import
{
DashboardListCtrl
}
from
'./dashboard_list_ctrl'
;
import
{
DashboardImportCtrl
}
from
'./dashboard_import_ctrl'
;
coreModule
.
controller
(
'DashboardListCtrl'
,
DashboardListCtrl
);
coreModule
.
controller
(
'DashboardImportCtrl'
,
DashboardImportCtrl
);
public/app/features/dashboard/dashboard_import_ctrl.ts
0 → 100644
View file @
cc2349f6
import
_
from
'lodash'
;
import
config
from
'app/core/config'
;
export
class
DashboardImportCtrl
{
navModel
:
any
;
step
:
number
;
jsonText
:
string
;
parseError
:
string
;
nameExists
:
boolean
;
dash
:
any
;
inputs
:
any
[];
inputsValid
:
boolean
;
gnetUrl
:
string
;
gnetError
:
string
;
gnetInfo
:
any
;
/** @ngInject */
constructor
(
private
backendSrv
,
navModelSrv
,
private
$location
,
private
$scope
,
$routeParams
)
{
this
.
navModel
=
navModelSrv
.
getNav
(
'create'
,
'import'
);
this
.
step
=
1
;
this
.
nameExists
=
false
;
// check gnetId in url
if
(
$routeParams
.
gnetId
)
{
this
.
gnetUrl
=
$routeParams
.
gnetId
;
this
.
checkGnetDashboard
();
}
}
onUpload
(
dash
)
{
this
.
dash
=
dash
;
this
.
dash
.
id
=
null
;
this
.
step
=
2
;
this
.
inputs
=
[];
if
(
this
.
dash
.
__inputs
)
{
for
(
let
input
of
this
.
dash
.
__inputs
)
{
var
inputModel
=
{
name
:
input
.
name
,
label
:
input
.
label
,
info
:
input
.
description
,
value
:
input
.
value
,
type
:
input
.
type
,
pluginId
:
input
.
pluginId
,
options
:
[]
};
if
(
input
.
type
===
'datasource'
)
{
this
.
setDatasourceOptions
(
input
,
inputModel
);
}
else
if
(
!
inputModel
.
info
)
{
inputModel
.
info
=
'Specify a string constant'
;
}
this
.
inputs
.
push
(
inputModel
);
}
}
this
.
inputsValid
=
this
.
inputs
.
length
===
0
;
this
.
titleChanged
();
}
setDatasourceOptions
(
input
,
inputModel
)
{
var
sources
=
_
.
filter
(
config
.
datasources
,
val
=>
{
return
val
.
type
===
input
.
pluginId
;
});
if
(
sources
.
length
===
0
)
{
inputModel
.
info
=
"No data sources of type "
+
input
.
pluginName
+
" found"
;
}
else
if
(
!
inputModel
.
info
)
{
inputModel
.
info
=
"Select a "
+
input
.
pluginName
+
" data source"
;
}
inputModel
.
options
=
sources
.
map
(
val
=>
{
return
{
text
:
val
.
name
,
value
:
val
.
name
};
});
}
inputValueChanged
()
{
this
.
inputsValid
=
true
;
for
(
let
input
of
this
.
inputs
)
{
if
(
!
input
.
value
)
{
this
.
inputsValid
=
false
;
}
}
}
titleChanged
()
{
this
.
backendSrv
.
search
({
query
:
this
.
dash
.
title
}).
then
(
res
=>
{
this
.
nameExists
=
false
;
for
(
let
hit
of
res
)
{
if
(
this
.
dash
.
title
===
hit
.
title
)
{
this
.
nameExists
=
true
;
break
;
}
}
});
}
saveDashboard
()
{
var
inputs
=
this
.
inputs
.
map
(
input
=>
{
return
{
name
:
input
.
name
,
type
:
input
.
type
,
pluginId
:
input
.
pluginId
,
value
:
input
.
value
};
});
return
this
.
backendSrv
.
post
(
'api/dashboards/import'
,
{
dashboard
:
this
.
dash
,
overwrite
:
true
,
inputs
:
inputs
}).
then
(
res
=>
{
this
.
$location
.
url
(
'dashboard/'
+
res
.
importedUri
);
this
.
$scope
.
dismiss
();
});
}
loadJsonText
()
{
try
{
this
.
parseError
=
''
;
var
dash
=
JSON
.
parse
(
this
.
jsonText
);
this
.
onUpload
(
dash
);
}
catch
(
err
)
{
console
.
log
(
err
);
this
.
parseError
=
err
.
message
;
return
;
}
}
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.com
res
.
json
.
gnetId
=
res
.
id
;
this
.
onUpload
(
res
.
json
);
}).
catch
(
err
=>
{
err
.
isHandled
=
true
;
this
.
gnetError
=
err
.
data
.
message
||
err
;
});
}
back
()
{
this
.
gnetUrl
=
''
;
this
.
step
=
1
;
this
.
gnetError
=
''
;
this
.
gnetInfo
=
''
;
}
}
public/app/features/dashboard/partials/dashboardImport.html
0 → 100644
View file @
cc2349f6
<page-header
model=
"ctrl.navModel"
></page-header>
<div
class=
"page-container page-body"
ng-cloak
>
<div
ng-if=
"ctrl.step === 1"
>
<form
class=
"gf-form-group"
>
<dash-upload
on-upload=
"ctrl.onUpload(dash)"
></dash-upload>
</form>
<h5
class=
"section-heading"
>
Grafana.com Dashboard
</h5>
<div
class=
"gf-form-group"
>
<div
class=
"gf-form gf-form--grow"
>
<input
type=
"text"
class=
"gf-form-input max-width-30"
ng-model=
"ctrl.gnetUrl"
placeholder=
"Paste Grafana.com 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"
>
<i
class=
"fa fa-warning"
></i>
{{ctrl.gnetError}}
</label>
</div>
</div>
<h5
class=
"section-heading"
>
Or paste JSON
</h5>
<div
class=
"gf-form-group"
>
<div
class=
"gf-form"
>
<textarea
rows=
"10"
data-share-panel-url=
""
class=
"gf-form-input"
ng-model=
"ctrl.jsonText"
></textarea>
</div>
<button
type=
"button"
class=
"btn btn-secondary"
ng-click=
"ctrl.loadJsonText()"
>
<i
class=
"fa fa-paste"
></i>
Load
</button>
<span
ng-if=
"ctrl.parseError"
class=
"text-error p-l-1"
>
<i
class=
"fa fa-warning"
></i>
{{ctrl.parseError}}
</span>
</div>
</div>
<div
ng-if=
"ctrl.step === 2"
>
<div
class=
"gf-form-group"
ng-if=
"ctrl.dash.gnetId"
>
<h3
class=
"section-heading"
>
Importing Dashboard from
<a
href=
"https://grafana.com/dashboards/{{ctrl.dash.gnetId}}"
class=
"external-link"
target=
"_blank"
>
Grafana.com
</a>
</h3>
<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>
<div
class=
"gf-form-group"
>
<div
class=
"gf-form-inline"
>
<div
class=
"gf-form gf-form--grow"
>
<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 || !ctrl.dash.title}"
>
<label
class=
"gf-form-label text-success"
ng-if=
"!ctrl.nameExists && ctrl.dash.title"
>
<i
class=
"fa fa-check"
></i>
</label>
</div>
</div>
<div
class=
"gf-form-inline"
ng-if=
"ctrl.nameExists"
>
<div
class=
"gf-form offset-width-15 gf-form--grow"
>
<label
class=
"gf-form-label text-warning gf-form-label--grow"
>
<i
class=
"fa fa-warning"
></i>
A Dashboard with the same name already exists
</label>
</div>
</div>
<div
class=
"gf-form-inline"
ng-if=
"!ctrl.dash.title"
>
<div
class=
"gf-form offset-width-15 gf-form--grow"
>
<label
class=
"gf-form-label text-warning gf-form-label--grow"
>
<i
class=
"fa fa-warning"
></i>
A Dashboard should have a name
</label>
</div>
</div>
<div
ng-repeat=
"input in ctrl.inputs"
>
<div
class=
"gf-form"
>
<label
class=
"gf-form-label width-15"
>
{{input.label}}
<info-popover
mode=
"right-normal"
>
{{input.info}}
</info-popover>
</label>
<!-- Data source input -->
<div
class=
"gf-form-select-wrapper"
style=
"width: 100%"
ng-if=
"input.type === 'datasource'"
>
<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>
<!-- Constant input -->
<input
ng-if=
"input.type === 'constant'"
type=
"text"
class=
"gf-form-input"
ng-model=
"input.value"
placeholder=
"{{input.default}}"
ng-change=
"ctrl.inputValueChanged()"
>
<label
class=
"gf-form-label text-success"
ng-show=
"input.value"
>
<i
class=
"fa fa-check"
></i>
</label>
</div>
</div>
</div>
<div
class=
"gf-form-button-row"
>
<button
type=
"button"
class=
"btn gf-form-btn btn-success width-12"
ng-click=
"ctrl.saveDashboard()"
ng-hide=
"ctrl.nameExists"
ng-disabled=
"!ctrl.inputsValid"
>
<i
class=
"fa fa-save"
></i>
Import
</button>
<button
type=
"button"
class=
"btn gf-form-btn btn-danger width-12"
ng-click=
"ctrl.saveDashboard()"
ng-show=
"ctrl.nameExists"
ng-disabled=
"!ctrl.inputsValid"
>
<i
class=
"fa fa-save"
></i>
Import (Overwrite)
</button>
<a
class=
"btn btn-link"
ng-click=
"ctrl.back()"
>
Cancel
</a>
</div>
</div>
</div>
public/app/features/dashboard/specs/dash
_import_ctrl_specs
.ts
→
public/app/features/dashboard/specs/dash
board_import_ctrl.jest
.ts
View file @
cc2349f6
import
{
describe
,
beforeEach
,
it
,
sinon
,
expect
,
angularMocks
}
from
'test/lib/common'
;
import
{
DashboardImportCtrl
}
from
'../dashboard_import_ctrl'
;
import
config
from
'../../../core/config'
;
import
{
DashImportCtrl
}
from
'app/features/dashboard/import/dash_import'
;
import
config
from
'app/core/config'
;
describe
(
'DashImportCtrl'
,
function
()
{
describe
(
'DashboardImportCtrl'
,
function
()
{
var
ctx
:
any
=
{};
var
backendSrv
=
{
search
:
sinon
.
stub
().
returns
(
Promise
.
resolve
([])),
get
:
sinon
.
stub
()
};
beforeEach
(
angularMocks
.
module
(
'grafana.core'
));
let
navModelSrv
;
let
backendSrv
;
beforeEach
(
angularMocks
.
inject
((
$rootScope
,
$controller
,
$q
)
=>
{
ctx
.
$q
=
$q
;
ctx
.
scope
=
$rootScope
.
$new
();
ctx
.
ctrl
=
$controller
(
DashImportCtrl
,
{
$scope
:
ctx
.
scope
,
backendSrv
:
backendSrv
,
});
}));
beforeEach
(()
=>
{
navModelSrv
=
{
getNav
:
()
=>
{}
};
backendSrv
=
{
search
:
jest
.
fn
().
mockReturnValue
(
Promise
.
resolve
([])),
get
:
jest
.
fn
()
};
ctx
.
ctrl
=
new
DashboardImportCtrl
(
backendSrv
,
navModelSrv
,
{},
{},
{});
});
describe
(
'when uploading json'
,
function
()
{
beforeEach
(
function
()
{
...
...
@@ -37,13 +36,13 @@ describe('DashImportCtrl', function() {
});
it
(
'should build input model'
,
function
()
{
expect
(
ctx
.
ctrl
.
inputs
.
length
).
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'
);
expect
(
ctx
.
ctrl
.
inputs
.
length
).
to
Be
(
1
);
expect
(
ctx
.
ctrl
.
inputs
[
0
].
name
).
to
Be
(
'ds'
);
expect
(
ctx
.
ctrl
.
inputs
[
0
].
info
).
to
Be
(
'Select a Test DB data source'
);
});
it
(
'should set inputValid to false'
,
function
()
{
expect
(
ctx
.
ctrl
.
inputsValid
).
to
.
eql
(
false
);
expect
(
ctx
.
ctrl
.
inputsValid
).
to
Be
(
false
);
});
});
...
...
@@ -51,7 +50,7 @@ describe('DashImportCtrl', function() {
beforeEach
(
function
()
{
ctx
.
ctrl
.
gnetUrl
=
'http://grafana.com/dashboards/123'
;
// setup api mock
backendSrv
.
get
=
sinon
.
spy
(()
=>
{
backendSrv
.
get
=
jest
.
fn
(()
=>
{
return
Promise
.
resolve
({
json
:
{}
});
...
...
@@ -60,7 +59,7 @@ describe('DashImportCtrl', function() {
});
it
(
'should call gnet api with correct dashboard id'
,
function
()
{
expect
(
backendSrv
.
get
.
getCall
(
0
).
args
[
0
]).
to
.
eql
(
'api/gnet/dashboards/123'
);
expect
(
backendSrv
.
get
.
mock
.
calls
[
0
][
0
]).
toBe
(
'api/gnet/dashboards/123'
);
});
});
...
...
@@ -68,7 +67,7 @@ describe('DashImportCtrl', function() {
beforeEach
(
function
()
{
ctx
.
ctrl
.
gnetUrl
=
'2342'
;
// setup api mock
backendSrv
.
get
=
sinon
.
spy
(()
=>
{
backendSrv
.
get
=
jest
.
fn
(()
=>
{
return
Promise
.
resolve
({
json
:
{}
});
...
...
@@ -77,10 +76,8 @@ describe('DashImportCtrl', function() {
});
it
(
'should call gnet api with correct dashboard id'
,
function
()
{
expect
(
backendSrv
.
get
.
getCall
(
0
).
args
[
0
]).
to
.
eql
(
'api/gnet/dashboards/2342'
);
expect
(
backendSrv
.
get
.
mock
.
calls
[
0
][
0
]).
toBe
(
'api/gnet/dashboards/2342'
);
});
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment