mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge pull request #12169 from alexanderzobnin/feat-10796
Import dashboard to folder
This commit is contained in:
@@ -57,4 +57,5 @@ type ImportDashboardCommand struct {
|
|||||||
Overwrite bool `json:"overwrite"`
|
Overwrite bool `json:"overwrite"`
|
||||||
Dashboard *simplejson.Json `json:"dashboard"`
|
Dashboard *simplejson.Json `json:"dashboard"`
|
||||||
Inputs []plugins.ImportDashboardInput `json:"inputs"`
|
Inputs []plugins.ImportDashboardInput `json:"inputs"`
|
||||||
|
FolderId int64 `json:"folderId"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,9 +99,10 @@ func setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, error) {
|
|||||||
|
|
||||||
if c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR {
|
if c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR {
|
||||||
children = append(children, &dtos.NavLink{Text: "Folder", SubTitle: "Create a new folder to organize your dashboards", Id: "folder", Icon: "gicon gicon-folder-new", Url: setting.AppSubUrl + "/dashboards/folder/new"})
|
children = append(children, &dtos.NavLink{Text: "Folder", SubTitle: "Create a new folder to organize your dashboards", Id: "folder", Icon: "gicon gicon-folder-new", Url: setting.AppSubUrl + "/dashboards/folder/new"})
|
||||||
children = append(children, &dtos.NavLink{Text: "Import", SubTitle: "Import dashboard from file or Grafana.com", Id: "import", Icon: "gicon gicon-dashboard-import", Url: setting.AppSubUrl + "/dashboard/import"})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
children = append(children, &dtos.NavLink{Text: "Import", SubTitle: "Import dashboard from file or Grafana.com", Id: "import", Icon: "gicon gicon-dashboard-import", Url: setting.AppSubUrl + "/dashboard/import"})
|
||||||
|
|
||||||
data.NavTree = append(data.NavTree, &dtos.NavLink{
|
data.NavTree = append(data.NavTree, &dtos.NavLink{
|
||||||
Text: "Create",
|
Text: "Create",
|
||||||
Id: "create",
|
Id: "create",
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ func ImportDashboard(c *m.ReqContext, apiCmd dtos.ImportDashboardCommand) Respon
|
|||||||
Path: apiCmd.Path,
|
Path: apiCmd.Path,
|
||||||
Inputs: apiCmd.Inputs,
|
Inputs: apiCmd.Inputs,
|
||||||
Overwrite: apiCmd.Overwrite,
|
Overwrite: apiCmd.Overwrite,
|
||||||
|
FolderId: apiCmd.FolderId,
|
||||||
Dashboard: apiCmd.Dashboard,
|
Dashboard: apiCmd.Dashboard,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ type ImportDashboardCommand struct {
|
|||||||
Path string
|
Path string
|
||||||
Inputs []ImportDashboardInput
|
Inputs []ImportDashboardInput
|
||||||
Overwrite bool
|
Overwrite bool
|
||||||
|
FolderId int64
|
||||||
|
|
||||||
OrgId int64
|
OrgId int64
|
||||||
User *m.SignedInUser
|
User *m.SignedInUser
|
||||||
@@ -70,7 +71,7 @@ func ImportDashboard(cmd *ImportDashboardCommand) error {
|
|||||||
UserId: cmd.User.UserId,
|
UserId: cmd.User.UserId,
|
||||||
Overwrite: cmd.Overwrite,
|
Overwrite: cmd.Overwrite,
|
||||||
PluginId: cmd.PluginId,
|
PluginId: cmd.PluginId,
|
||||||
FolderId: dashboard.FolderId,
|
FolderId: cmd.FolderId,
|
||||||
}
|
}
|
||||||
|
|
||||||
dto := &dashboards.SaveDashboardDTO{
|
dto := &dashboards.SaveDashboardDTO{
|
||||||
@@ -91,6 +92,7 @@ func ImportDashboard(cmd *ImportDashboardCommand) error {
|
|||||||
Title: savedDash.Title,
|
Title: savedDash.Title,
|
||||||
Path: cmd.Path,
|
Path: cmd.Path,
|
||||||
Revision: savedDash.Data.Get("revision").MustInt64(1),
|
Revision: savedDash.Data.Get("revision").MustInt64(1),
|
||||||
|
FolderId: savedDash.FolderId,
|
||||||
ImportedUri: "db/" + savedDash.Slug,
|
ImportedUri: "db/" + savedDash.Slug,
|
||||||
ImportedUrl: savedDash.GetUrl(),
|
ImportedUrl: savedDash.GetUrl(),
|
||||||
ImportedRevision: dashboard.Data.Get("revision").MustInt64(1),
|
ImportedRevision: dashboard.Data.Get("revision").MustInt64(1),
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ type PluginDashboardInfoDTO struct {
|
|||||||
ImportedUrl string `json:"importedUrl"`
|
ImportedUrl string `json:"importedUrl"`
|
||||||
Slug string `json:"slug"`
|
Slug string `json:"slug"`
|
||||||
DashboardId int64 `json:"dashboardId"`
|
DashboardId int64 `json:"dashboardId"`
|
||||||
|
FolderId int64 `json:"folderId"`
|
||||||
ImportedRevision int64 `json:"importedRevision"`
|
ImportedRevision int64 `json:"importedRevision"`
|
||||||
Revision int64 `json:"revision"`
|
Revision int64 `json:"revision"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
|
|||||||
@@ -13,6 +13,10 @@
|
|||||||
<i class="fa fa-plus"></i>
|
<i class="fa fa-plus"></i>
|
||||||
Folder
|
Folder
|
||||||
</a>
|
</a>
|
||||||
|
<a class="btn btn-success" href="{{ctrl.importDashboardUrl()}}" ng-if="ctrl.hasEditPermissionInFolders || ctrl.canSave">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
Import
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="page-action-bar page-action-bar--narrow" ng-show="ctrl.hasFilters">
|
<div class="page-action-bar page-action-bar--narrow" ng-show="ctrl.hasFilters">
|
||||||
|
|||||||
@@ -294,6 +294,16 @@ export class ManageDashboardsCtrl {
|
|||||||
|
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
importDashboardUrl() {
|
||||||
|
let url = 'dashboard/import';
|
||||||
|
|
||||||
|
if (this.folderId) {
|
||||||
|
url += `?folderId=${this.folderId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function manageDashboardsDirective() {
|
export function manageDashboardsDirective() {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
<a href="dashboards/folder/new" class="search-filter-box-link" ng-if="ctrl.isEditor">
|
<a href="dashboards/folder/new" class="search-filter-box-link" ng-if="ctrl.isEditor">
|
||||||
<i class="gicon gicon-folder-new"></i> New folder
|
<i class="gicon gicon-folder-new"></i> New folder
|
||||||
</a>
|
</a>
|
||||||
<a href="dashboard/import" class="search-filter-box-link" ng-if="ctrl.isEditor">
|
<a href="dashboard/import" class="search-filter-box-link" ng-if="ctrl.isEditor || ctrl.hasEditPermissionInFolders">
|
||||||
<i class="gicon gicon-dashboard-import"></i> Import dashboard
|
<i class="gicon gicon-dashboard-import"></i> Import dashboard
|
||||||
</a>
|
</a>
|
||||||
<a class="search-filter-box-link" target="_blank" href="https://grafana.com/dashboards?utm_source=grafana_search">
|
<a class="search-filter-box-link" target="_blank" href="https://grafana.com/dashboards?utm_source=grafana_search">
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ export class DashboardImportCtrl {
|
|||||||
uidValidationError: any;
|
uidValidationError: any;
|
||||||
autoGenerateUid: boolean;
|
autoGenerateUid: boolean;
|
||||||
autoGenerateUidValue: string;
|
autoGenerateUidValue: string;
|
||||||
|
folderId: number;
|
||||||
|
initialFolderTitle: string;
|
||||||
|
isValidFolderSelection: boolean;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private backendSrv, private validationSrv, navModelSrv, private $location, $routeParams) {
|
constructor(private backendSrv, private validationSrv, navModelSrv, private $location, $routeParams) {
|
||||||
@@ -31,6 +34,8 @@ export class DashboardImportCtrl {
|
|||||||
this.uidExists = false;
|
this.uidExists = false;
|
||||||
this.autoGenerateUid = true;
|
this.autoGenerateUid = true;
|
||||||
this.autoGenerateUidValue = 'auto-generated';
|
this.autoGenerateUidValue = 'auto-generated';
|
||||||
|
this.folderId = $routeParams.folderId ? Number($routeParams.folderId) || 0 : null;
|
||||||
|
this.initialFolderTitle = 'Select a folder';
|
||||||
|
|
||||||
// check gnetId in url
|
// check gnetId in url
|
||||||
if ($routeParams.gnetId) {
|
if ($routeParams.gnetId) {
|
||||||
@@ -102,8 +107,9 @@ export class DashboardImportCtrl {
|
|||||||
this.nameExists = false;
|
this.nameExists = false;
|
||||||
|
|
||||||
this.validationSrv
|
this.validationSrv
|
||||||
.validateNewDashboardName(0, this.dash.title)
|
.validateNewDashboardName(this.folderId, this.dash.title)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
this.nameExists = false;
|
||||||
this.hasNameValidationError = false;
|
this.hasNameValidationError = false;
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
@@ -138,6 +144,23 @@ export class DashboardImportCtrl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onFolderChange(folder) {
|
||||||
|
this.folderId = folder.id;
|
||||||
|
this.titleChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
onEnterFolderCreation() {
|
||||||
|
this.inputsValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onExitFolderCreation() {
|
||||||
|
this.inputValueChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
isValid() {
|
||||||
|
return this.inputsValid && this.folderId !== null;
|
||||||
|
}
|
||||||
|
|
||||||
saveDashboard() {
|
saveDashboard() {
|
||||||
var inputs = this.inputs.map(input => {
|
var inputs = this.inputs.map(input => {
|
||||||
return {
|
return {
|
||||||
@@ -153,6 +176,7 @@ export class DashboardImportCtrl {
|
|||||||
dashboard: this.dash,
|
dashboard: this.dash,
|
||||||
overwrite: true,
|
overwrite: true,
|
||||||
inputs: inputs,
|
inputs: inputs,
|
||||||
|
folderId: this.folderId,
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
this.$location.url(res.importedUrl);
|
this.$location.url(res.importedUrl);
|
||||||
|
|||||||
@@ -132,24 +132,27 @@ export class FolderPickerCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private loadInitialValue() {
|
private loadInitialValue() {
|
||||||
if (this.initialFolderId && this.initialFolderId > 0) {
|
const resetFolder = { text: this.initialTitle, value: null };
|
||||||
|
const rootFolder = { text: this.rootName, value: 0 };
|
||||||
this.getOptions('').then(result => {
|
this.getOptions('').then(result => {
|
||||||
this.folder = _.find(result, { value: this.initialFolderId });
|
let folder;
|
||||||
if (!this.folder) {
|
if (this.initialFolderId) {
|
||||||
this.folder = { text: this.initialTitle, value: this.initialFolderId };
|
folder = _.find(result, { value: this.initialFolderId });
|
||||||
}
|
} else if (this.enableReset && this.initialTitle && this.initialFolderId === null) {
|
||||||
this.onFolderLoad();
|
folder = resetFolder;
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (this.initialTitle && this.initialFolderId === null) {
|
|
||||||
this.folder = { text: this.initialTitle, value: null };
|
|
||||||
} else {
|
|
||||||
this.folder = { text: this.rootName, value: 0 };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onFolderLoad();
|
if (!folder) {
|
||||||
|
if (this.isEditor) {
|
||||||
|
folder = rootFolder;
|
||||||
|
} else {
|
||||||
|
folder = result.length > 0 ? result[0] : resetFolder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.folder = folder;
|
||||||
|
this.onFolderLoad();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private onFolderLoad() {
|
private onFolderLoad() {
|
||||||
if (this.onLoad) {
|
if (this.onLoad) {
|
||||||
|
|||||||
@@ -80,6 +80,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form-inline">
|
||||||
|
<div class="gf-form gf-form--grow">
|
||||||
|
<folder-picker label-class="width-15"
|
||||||
|
initial-folder-id="ctrl.folderId"
|
||||||
|
initial-title="ctrl.initialFolderTitle"
|
||||||
|
on-change="ctrl.onFolderChange($folder)"
|
||||||
|
on-load="ctrl.onFolderChange($folder)"
|
||||||
|
enter-folder-creation="ctrl.onEnterFolderCreation()"
|
||||||
|
exit-folder-creation="ctrl.onExitFolderCreation()"
|
||||||
|
enable-create-new="true">
|
||||||
|
</folder-picker>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="gf-form-inline">
|
<div class="gf-form-inline">
|
||||||
<div class="gf-form gf-form--grow">
|
<div class="gf-form gf-form--grow">
|
||||||
<span class="gf-form-label width-15">
|
<span class="gf-form-label width-15">
|
||||||
@@ -132,10 +146,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form-button-row">
|
<div class="gf-form-button-row">
|
||||||
<button type="button" class="btn btn-success width-12" ng-click="ctrl.saveDashboard()" ng-hide="ctrl.nameExists || ctrl.uidExists" ng-disabled="!ctrl.inputsValid">
|
<button type="button" class="btn btn-success width-12" ng-click="ctrl.saveDashboard()" ng-hide="ctrl.nameExists || ctrl.uidExists" ng-disabled="!ctrl.isValid()">
|
||||||
<i class="fa fa-save"></i> Import
|
<i class="fa fa-save"></i> Import
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-danger width-12" ng-click="ctrl.saveDashboard()" ng-show="ctrl.nameExists || ctrl.uidExists" ng-disabled="!ctrl.inputsValid">
|
<button type="button" class="btn btn-danger width-12" ng-click="ctrl.saveDashboard()" ng-show="ctrl.nameExists || ctrl.uidExists" ng-disabled="!ctrl.isValid()">
|
||||||
<i class="fa fa-save"></i> Import (Overwrite)
|
<i class="fa fa-save"></i> Import (Overwrite)
|
||||||
</button>
|
</button>
|
||||||
<a class="btn btn-link" ng-click="ctrl.back()">Cancel</a>
|
<a class="btn btn-link" ng-click="ctrl.back()">Cancel</a>
|
||||||
|
|||||||
Reference in New Issue
Block a user