diff --git a/public/app/containers/ManageDashboards/FolderPermissions.tsx b/public/app/containers/ManageDashboards/FolderPermissions.tsx index 93b9520739e..1af58b44bcc 100644 --- a/public/app/containers/ManageDashboards/FolderPermissions.tsx +++ b/public/app/containers/ManageDashboards/FolderPermissions.tsx @@ -20,7 +20,7 @@ export class FolderPermissions extends Component { loadStore() { const { nav, folder, view } = this.props; return folder.load(view.routeParams.get('uid') as string).then(res => { - view.updatePathAndQuery(`${res.meta.url}/permissions`, {}, {}); + view.updatePathAndQuery(`${res.url}/permissions`, {}, {}); return nav.initFolderNav(toJS(folder.folder), 'manage-folder-permissions'); }); } diff --git a/public/app/containers/ManageDashboards/FolderSettings.jest.tsx b/public/app/containers/ManageDashboards/FolderSettings.jest.tsx index bf7b35ed05d..72ba9ff6a7b 100644 --- a/public/app/containers/ManageDashboards/FolderSettings.jest.tsx +++ b/public/app/containers/ManageDashboards/FolderSettings.jest.tsx @@ -9,16 +9,14 @@ describe('FolderSettings', () => { let page; beforeAll(() => { - backendSrv.getDashboardByUid.mockReturnValue( + backendSrv.getFolderByUid.mockReturnValue( Promise.resolve({ - dashboard: { - id: 1, - title: 'Folder Name', - }, - meta: { - url: '/dashboards/f/uid/folder-name', - canSave: true, - }, + id: 1, + uid: 'uid', + title: 'Folder Name', + url: '/dashboards/f/uid/folder-name', + canSave: true, + version: 1, }) ); diff --git a/public/app/containers/ManageDashboards/FolderSettings.tsx b/public/app/containers/ManageDashboards/FolderSettings.tsx index a6349764a14..586a8f05b4c 100644 --- a/public/app/containers/ManageDashboards/FolderSettings.tsx +++ b/public/app/containers/ManageDashboards/FolderSettings.tsx @@ -10,7 +10,6 @@ import appEvents from 'app/core/app_events'; @observer export class FolderSettings extends React.Component { formSnapshot: any; - dashboard: any; constructor(props) { super(props); @@ -22,9 +21,7 @@ export class FolderSettings extends React.Component { return folder.load(view.routeParams.get('uid') as string).then(res => { this.formSnapshot = getSnapshot(folder); - this.dashboard = res.dashboard; - - view.updatePathAndQuery(`${res.meta.url}/settings`, {}, {}); + view.updatePathAndQuery(`${res.url}/settings`, {}, {}); return nav.initFolderNav(toJS(folder.folder), 'manage-folder-settings'); }); @@ -51,7 +48,7 @@ export class FolderSettings extends React.Component { const { nav, folder, view } = this.props; folder - .saveFolder(this.dashboard, { overwrite: false }) + .saveFolder({ overwrite: false }) .then(newUrl => { view.updatePathAndQuery(newUrl, {}, {}); @@ -61,7 +58,7 @@ export class FolderSettings extends React.Component { .then(() => { return nav.initFolderNav(toJS(folder.folder), 'manage-folder-settings'); }) - .catch(this.handleSaveFolderError); + .catch(this.handleSaveFolderError.bind(this)); } delete(evt) { @@ -79,7 +76,7 @@ export class FolderSettings extends React.Component { icon: 'fa-trash', yesText: 'Delete', onConfirm: () => { - return this.props.folder.deleteFolder().then(() => { + return folder.deleteFolder().then(() => { appEvents.emit('alert-success', ['Folder Deleted', `${title} has been deleted`]); view.updatePathAndQuery('dashboards', '', ''); }); @@ -91,6 +88,8 @@ export class FolderSettings extends React.Component { if (err.data && err.data.status === 'version-mismatch') { err.isHandled = true; + const { nav, folder, view } = this.props; + appEvents.emit('confirm-modal', { title: 'Conflict', text: 'Someone else has updated this folder.', @@ -98,16 +97,20 @@ export class FolderSettings extends React.Component { yesText: 'Save & Overwrite', icon: 'fa-warning', onConfirm: () => { - this.props.folder.saveFolder(this.dashboard, { overwrite: true }); + folder + .saveFolder({ overwrite: true }) + .then(newUrl => { + view.updatePathAndQuery(newUrl, {}, {}); + + appEvents.emit('dashboard-saved'); + appEvents.emit('alert-success', ['Folder saved']); + }) + .then(() => { + return nav.initFolderNav(toJS(folder.folder), 'manage-folder-settings'); + }); }, }); } - - if (err.data && err.data.status === 'name-exists') { - err.isHandled = true; - - appEvents.emit('alert-error', ['A folder or dashboard with this name exists already.']); - } } render() { diff --git a/public/app/core/components/manage_dashboards/manage_dashboards.ts b/public/app/core/components/manage_dashboards/manage_dashboards.ts index d0f905f5f16..448b82cce54 100644 --- a/public/app/core/components/manage_dashboards/manage_dashboards.ts +++ b/public/app/core/components/manage_dashboards/manage_dashboards.ts @@ -173,17 +173,16 @@ export class ManageDashboardsCtrl { icon: 'fa-trash', yesText: 'Delete', onConfirm: () => { - const foldersAndDashboards = data.folders.concat(data.dashboards); - this.deleteFoldersAndDashboards(foldersAndDashboards); + this.deleteFoldersAndDashboards(data.folders, data.dashboards); }, }); } - private deleteFoldersAndDashboards(uids) { - this.backendSrv.deleteDashboards(uids).then(result => { - const folders = _.filter(result, dash => dash.meta.isFolder); + private deleteFoldersAndDashboards(folderUids, dashboardUids) { + this.backendSrv.deleteFoldersAndDashboards(folderUids, dashboardUids).then(result => { + const folders = _.filter(result, dash => !dash.dashboard); const folderCount = folders.length; - const dashboards = _.filter(result, dash => !dash.meta.isFolder); + const dashboards = _.filter(result, dash => dash.dashboard); const dashCount = dashboards.length; if (result.length > 0) { @@ -198,7 +197,7 @@ export class ManageDashboardsCtrl { header = `Folder${folderCount === 1 ? '' : 's'} Deleted`; if (folderCount === 1) { - msg = `${folders[0].dashboard.title} has been deleted`; + msg = `${folders[0].title} has been deleted`; } else { msg = `${folderCount} folder${folderCount === 1 ? '' : 's'} has been deleted`; } diff --git a/public/app/core/services/backend_srv.ts b/public/app/core/services/backend_srv.ts index 0a8b305ea53..b9712d330d9 100644 --- a/public/app/core/services/backend_srv.ts +++ b/public/app/core/services/backend_srv.ts @@ -229,6 +229,10 @@ export class BackendSrv { return this.get(`/api/dashboards/uid/${uid}`); } + getFolderByUid(uid: string) { + return this.get(`/api/folders/${uid}`); + } + saveDashboard(dash, options) { options = options || {}; @@ -240,34 +244,36 @@ export class BackendSrv { }); } - createDashboardFolder(name) { - const dash = { - schemaVersion: 16, - title: name.trim(), - editable: true, - panels: [], - }; - - return this.post('/api/dashboards/db/', { - dashboard: dash, - isFolder: true, - overwrite: false, - }).then(res => { - return this.getDashboard('db', res.slug); - }); + createFolder(payload: any) { + return this.post('/api/folders', payload); } - saveFolder(dash, options) { + updateFolder(folder, options) { options = options || {}; - return this.post('/api/dashboards/db/', { - dashboard: dash, - isFolder: true, + return this.put(`/api/folders/${folder.uid}`, { + title: folder.title, + version: folder.version, overwrite: options.overwrite === true, - message: options.message || '', }); } + deleteFolder(uid: string) { + let deferred = this.$q.defer(); + + this.getFolderByUid(uid).then(folder => { + this.delete(`/api/folders/${uid}`) + .then(() => { + deferred.resolve(folder); + }) + .catch(err => { + deferred.reject(err); + }); + }); + + return deferred.promise; + } + deleteDashboard(uid) { let deferred = this.$q.defer(); @@ -284,11 +290,15 @@ export class BackendSrv { return deferred.promise; } - deleteDashboards(dashboardUids) { + deleteFoldersAndDashboards(folderUids, dashboardUids) { const tasks = []; - for (let uid of dashboardUids) { - tasks.push(this.createTask(this.deleteDashboard.bind(this), true, uid)); + for (let folderUid of folderUids) { + tasks.push(this.createTask(this.deleteFolder.bind(this), true, folderUid)); + } + + for (let dashboardUid of dashboardUids) { + tasks.push(this.createTask(this.deleteDashboard.bind(this), true, dashboardUid)); } return this.executeInOrder(tasks, []); diff --git a/public/app/features/dashboard/create_folder_ctrl.ts b/public/app/features/dashboard/create_folder_ctrl.ts index f3d9167278b..0d6a92d24e3 100644 --- a/public/app/features/dashboard/create_folder_ctrl.ts +++ b/public/app/features/dashboard/create_folder_ctrl.ts @@ -17,9 +17,9 @@ export class CreateFolderCtrl { return; } - return this.backendSrv.createDashboardFolder(this.title).then(result => { + return this.backendSrv.createFolder({ title: this.title }).then(result => { appEvents.emit('alert-success', ['Folder Created', 'OK']); - this.$location.url(result.meta.url); + this.$location.url(result.url); }); } diff --git a/public/app/features/dashboard/folder_dashboards_ctrl.ts b/public/app/features/dashboard/folder_dashboards_ctrl.ts index 8ee942445ae..05cc420c489 100644 --- a/public/app/features/dashboard/folder_dashboards_ctrl.ts +++ b/public/app/features/dashboard/folder_dashboards_ctrl.ts @@ -14,7 +14,7 @@ export class FolderDashboardsCtrl { const loader = new FolderPageLoader(this.backendSrv); loader.load(this, this.uid, 'manage-folder-dashboards').then(folder => { - const url = locationUtil.stripBaseFromUrl(folder.meta.url); + const url = locationUtil.stripBaseFromUrl(folder.url); if (url !== $location.path()) { $location.path(url).replace(); diff --git a/public/app/features/dashboard/folder_page_loader.ts b/public/app/features/dashboard/folder_page_loader.ts index 81d10068361..6842c61847e 100755 --- a/public/app/features/dashboard/folder_page_loader.ts +++ b/public/app/features/dashboard/folder_page_loader.ts @@ -36,16 +36,16 @@ export class FolderPageLoader { }, }; - return this.backendSrv.getDashboardByUid(uid).then(result => { - ctrl.folderId = result.dashboard.id; - const folderTitle = result.dashboard.title; - const folderUrl = result.meta.url; + return this.backendSrv.getFolderByUid(uid).then(folder => { + ctrl.folderId = folder.id; + const folderTitle = folder.title; + const folderUrl = folder.url; ctrl.navModel.main.text = folderTitle; const dashTab = ctrl.navModel.main.children.find(child => child.id === 'manage-folder-dashboards'); dashTab.url = folderUrl; - if (result.meta.canAdmin) { + if (folder.canAdmin) { const permTab = ctrl.navModel.main.children.find(child => child.id === 'manage-folder-permissions'); permTab.url = folderUrl + '/permissions'; @@ -55,7 +55,7 @@ export class FolderPageLoader { ctrl.navModel.main.children = [dashTab]; } - return result; + return folder; }); } } diff --git a/public/app/features/dashboard/folder_settings_ctrl.ts b/public/app/features/dashboard/folder_settings_ctrl.ts index 004ba2efa9f..a847c29ac56 100644 --- a/public/app/features/dashboard/folder_settings_ctrl.ts +++ b/public/app/features/dashboard/folder_settings_ctrl.ts @@ -7,8 +7,7 @@ export class FolderSettingsCtrl { folderId: number; uid: string; canSave = false; - dashboard: any; - meta: any; + folder: any; title: string; hasChanged: boolean; @@ -23,10 +22,9 @@ export class FolderSettingsCtrl { $location.path(`${folder.meta.url}/settings`).replace(); } - this.dashboard = folder.dashboard; - this.meta = folder.meta; - this.canSave = folder.meta.canSave; - this.title = this.dashboard.title; + this.folder = folder; + this.canSave = this.folder.canSave; + this.title = this.folder.title; }); } } @@ -38,10 +36,10 @@ export class FolderSettingsCtrl { return; } - this.dashboard.title = this.title.trim(); + this.folder.title = this.title.trim(); return this.backendSrv - .updateDashboardFolder(this.dashboard, { overwrite: false }) + .updateFolder(this.folder) .then(result => { if (result.url !== this.$location.path()) { this.$location.url(result.url + '/settings'); @@ -54,7 +52,7 @@ export class FolderSettingsCtrl { } titleChanged() { - this.hasChanged = this.dashboard.title.toLowerCase() !== this.title.trim().toLowerCase(); + this.hasChanged = this.folder.title.toLowerCase() !== this.title.trim().toLowerCase(); } delete(evt) { @@ -69,8 +67,8 @@ export class FolderSettingsCtrl { icon: 'fa-trash', yesText: 'Delete', onConfirm: () => { - return this.backendSrv.deleteDashboard(this.dashboard.uid).then(() => { - appEvents.emit('alert-success', ['Folder Deleted', `${this.dashboard.title} has been deleted`]); + return this.backendSrv.deleteFolder(this.uid).then(() => { + appEvents.emit('alert-success', ['Folder Deleted', `${this.folder.title} has been deleted`]); this.$location.url('dashboards'); }); }, @@ -88,15 +86,9 @@ export class FolderSettingsCtrl { yesText: 'Save & Overwrite', icon: 'fa-warning', onConfirm: () => { - this.backendSrv.updateDashboardFolder(this.dashboard, { overwrite: true }); + this.backendSrv.updateFolder(this.folder, { overwrite: true }); }, }); } - - if (err.data && err.data.status === 'name-exists') { - err.isHandled = true; - - appEvents.emit('alert-error', ['A folder or dashboard with this name exists already.']); - } } } diff --git a/public/app/stores/FolderStore/FolderStore.ts b/public/app/stores/FolderStore/FolderStore.ts index 6f14e7221f8..4e2066f16e5 100644 --- a/public/app/stores/FolderStore/FolderStore.ts +++ b/public/app/stores/FolderStore/FolderStore.ts @@ -2,10 +2,12 @@ import { types, getEnv, flow } from 'mobx-state-tree'; export const Folder = types.model('Folder', { id: types.identifier(types.number), + uid: types.string, title: types.string, url: types.string, canSave: types.boolean, hasChanged: types.boolean, + version: types.number, }); export const FolderStore = types @@ -15,13 +17,15 @@ export const FolderStore = types .actions(self => ({ load: flow(function* load(uid: string) { const backendSrv = getEnv(self).backendSrv; - const res = yield backendSrv.getDashboardByUid(uid); + const res = yield backendSrv.getFolderByUid(uid); self.folder = Folder.create({ - id: res.dashboard.id, - title: res.dashboard.title, - url: res.meta.url, - canSave: res.meta.canSave, + id: res.id, + uid: res.uid, + title: res.title, + url: res.url, + canSave: res.canSave, hasChanged: false, + version: res.version, }); return res; }), @@ -31,12 +35,13 @@ export const FolderStore = types self.folder.hasChanged = originalTitle.toLowerCase() !== title.trim().toLowerCase() && title.trim().length > 0; }, - saveFolder: flow(function* saveFolder(dashboard: any, options: any) { + saveFolder: flow(function* saveFolder(options: any) { const backendSrv = getEnv(self).backendSrv; - dashboard.title = self.folder.title.trim(); + self.folder.title = self.folder.title.trim(); - const res = yield backendSrv.saveFolder(dashboard, options); + const res = yield backendSrv.updateFolder(self.folder, options); self.folder.url = res.url; + self.folder.version = res.version; return `${self.folder.url}/settings`; }), @@ -44,6 +49,6 @@ export const FolderStore = types deleteFolder: flow(function* deleteFolder() { const backendSrv = getEnv(self).backendSrv; - return backendSrv.deleteDashboard(self.folder.url); + return backendSrv.deleteFolder(self.folder.uid); }), })); diff --git a/public/test/mocks/common.ts b/public/test/mocks/common.ts index 3f80227435d..1531f2ed176 100644 --- a/public/test/mocks/common.ts +++ b/public/test/mocks/common.ts @@ -2,6 +2,7 @@ export const backendSrv = { get: jest.fn(), getDashboard: jest.fn(), getDashboardByUid: jest.fn(), + getFolderByUid: jest.fn(), post: jest.fn(), };