mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
* Chore: Remove angular dependency from backendSrv * Refactor: Naive soultion for logging out unauthorized users * Refactor: Restructures to different streams * Refactor: Restructures datasourceRequest * Refactor: Flipped back if statement * Refactor: Extracted getFromFetchStream * Refactor: Extracts toFailureStream operation * Refactor: Fixes issue when options.params contains arrays * Refactor: Fixes broken test (but we need a lot more) * Refactor: Adds explaining comments * Refactor: Adds latest RxJs version so cancellations work * Refactor: Cleans up the takeUntil code * Refactor: Adds tests for request function * Refactor: Separates into smaller functions * Refactor: Adds last error tests * Started to changed so we require getBackendSrv from the @grafana-runtime when applicable. * Using the getBackendSrv from @grafana/runtime. * Changed so we use the getBackendSrv from the @grafana-runtime when possible. * Fixed so Server Admin -> Orgs works again. * Removed unused dependency. * Fixed digest issues on the Server Admin -> Users page. * Fix: Fixes digest problems in Playlists * Fix: Fixes digest issues in VersionHistory * Tests: Fixes broken tests * Fix: Fixes digest issues in Alerting => Notification channels * Fixed digest issues on the Intive page. * Fixed so we run digest after password reset email sent. * Fixed digest issue when trying to sign up account. * Fixed so the Server Admin -> Edit Org works with backendSrv * Fixed so Server Admin -> Users works with backend srv. * Fixed digest issues in Server Admin -> Orgs * Fix: Fixes digest issues in DashList plugin * Fixed digest issues on Server Admin -> users. * Fix: Fixes digest issues with Snapshots * Fixed digest issue when deleting a user. * Fix: Fixes digest issues with dashLink * Chore: Changes RxJs version to 6.5.4 which includes the same cancellation fix * Fix: Fixes digest issue when toggling folder in manage dashboards * Fix: Fixes bug in executeInOrder * Fix: Fixes digest issue with CreateFolderCtrl and FolderDashboardsCtrl * Fix: Fixes tslint error in test * Refactor: Changes default behaviour for emitted messages as before migration * Fix: Fixes various digest issues when saving, starring or deleting dashboards * Fix: Fixes digest issues with FolderPickerCtrl * Fixed digest issue. * Fixed digest issues. * Fixed issues with angular digest. * Removed the this.digest pattern. Co-authored-by: Hugo Häggmark <hugo.haggmark@gmail.com> Co-authored-by: Marcus Andersson <systemvetaren@gmail.com>
267 lines
7.6 KiB
TypeScript
267 lines
7.6 KiB
TypeScript
import { ILocationService } from 'angular';
|
|
import { AppEvents, PanelEvents } from '@grafana/data';
|
|
|
|
import coreModule from 'app/core/core_module';
|
|
import { appEvents } from 'app/core/app_events';
|
|
import locationUtil from 'app/core/utils/location_util';
|
|
import { DashboardModel } from '../state/DashboardModel';
|
|
import { removePanel } from '../utils/panel';
|
|
import { CoreEvents, DashboardMeta } from 'app/types';
|
|
import { GrafanaRootScope } from 'app/routes/GrafanaCtrl';
|
|
import { backendSrv } from 'app/core/services/backend_srv';
|
|
import { promiseToDigest } from '../../../core/utils/promiseToDigest';
|
|
|
|
interface DashboardSaveOptions {
|
|
folderId?: number;
|
|
overwrite?: boolean;
|
|
message?: string;
|
|
makeEditable?: boolean;
|
|
}
|
|
|
|
export class DashboardSrv {
|
|
dashboard: DashboardModel;
|
|
|
|
/** @ngInject */
|
|
constructor(private $rootScope: GrafanaRootScope, private $location: ILocationService) {
|
|
appEvents.on(CoreEvents.saveDashboard, this.saveDashboard.bind(this), $rootScope);
|
|
appEvents.on(PanelEvents.panelChangeView, this.onPanelChangeView);
|
|
appEvents.on(CoreEvents.removePanel, this.onRemovePanel);
|
|
}
|
|
|
|
create(dashboard: any, meta: DashboardMeta) {
|
|
return new DashboardModel(dashboard, meta);
|
|
}
|
|
|
|
setCurrent(dashboard: DashboardModel) {
|
|
this.dashboard = dashboard;
|
|
}
|
|
|
|
getCurrent(): DashboardModel {
|
|
return this.dashboard;
|
|
}
|
|
|
|
onRemovePanel = (panelId: number) => {
|
|
const dashboard = this.getCurrent();
|
|
removePanel(dashboard, dashboard.getPanelById(panelId), true);
|
|
};
|
|
|
|
onPanelChangeView = ({
|
|
fullscreen = false,
|
|
edit = false,
|
|
panelId,
|
|
}: {
|
|
fullscreen?: boolean;
|
|
edit?: boolean;
|
|
panelId?: number;
|
|
}) => {
|
|
const urlParams = this.$location.search();
|
|
|
|
// handle toggle logic
|
|
// I hate using these truthy converters (!!) but in this case
|
|
// I think it's appropriate. edit can be null/false/undefined and
|
|
// here i want all of those to compare the same
|
|
if (fullscreen === urlParams.fullscreen && edit === !!urlParams.edit) {
|
|
const paramsToRemove = ['fullscreen', 'edit', 'panelId', 'tab'];
|
|
for (const key of paramsToRemove) {
|
|
delete urlParams[key];
|
|
}
|
|
|
|
this.$location.search(urlParams);
|
|
return;
|
|
}
|
|
|
|
const newUrlParams = {
|
|
...urlParams,
|
|
fullscreen: fullscreen || undefined,
|
|
edit: edit || undefined,
|
|
tab: edit ? urlParams.tab : undefined,
|
|
panelId,
|
|
};
|
|
|
|
Object.keys(newUrlParams).forEach(key => {
|
|
if (newUrlParams[key] === undefined) {
|
|
delete newUrlParams[key];
|
|
}
|
|
});
|
|
|
|
this.$location.search(newUrlParams);
|
|
};
|
|
|
|
handleSaveDashboardError(
|
|
clone: any,
|
|
options: DashboardSaveOptions,
|
|
err: { data: { status: string; message: any }; isHandled: boolean }
|
|
) {
|
|
options.overwrite = true;
|
|
|
|
if (err.data && err.data.status === 'version-mismatch') {
|
|
err.isHandled = true;
|
|
|
|
this.$rootScope.appEvent(CoreEvents.showConfirmModal, {
|
|
title: 'Conflict',
|
|
text: 'Someone else has updated this dashboard.',
|
|
text2: 'Would you still like to save this dashboard?',
|
|
yesText: 'Save & Overwrite',
|
|
icon: 'fa-warning',
|
|
onConfirm: () => {
|
|
this.save(clone, options);
|
|
},
|
|
});
|
|
}
|
|
|
|
if (err.data && err.data.status === 'name-exists') {
|
|
err.isHandled = true;
|
|
|
|
this.$rootScope.appEvent(CoreEvents.showConfirmModal, {
|
|
title: 'Conflict',
|
|
text: 'A dashboard with the same name in selected folder already exists.',
|
|
text2: 'Would you still like to save this dashboard?',
|
|
yesText: 'Save & Overwrite',
|
|
icon: 'fa-warning',
|
|
onConfirm: () => {
|
|
this.save(clone, options);
|
|
},
|
|
});
|
|
}
|
|
|
|
if (err.data && err.data.status === 'plugin-dashboard') {
|
|
err.isHandled = true;
|
|
|
|
this.$rootScope.appEvent(CoreEvents.showConfirmModal, {
|
|
title: 'Plugin Dashboard',
|
|
text: err.data.message,
|
|
text2: 'Your changes will be lost when you update the plugin. Use Save As to create custom version.',
|
|
yesText: 'Overwrite',
|
|
icon: 'fa-warning',
|
|
altActionText: 'Save As',
|
|
onAltAction: () => {
|
|
this.showSaveAsModal();
|
|
},
|
|
onConfirm: () => {
|
|
this.save(clone, { ...options, overwrite: true });
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
postSave(data: { version: number; url: string }) {
|
|
this.dashboard.version = data.version;
|
|
|
|
// important that these happen before location redirect below
|
|
this.$rootScope.appEvent(CoreEvents.dashboardSaved, this.dashboard);
|
|
this.$rootScope.appEvent(AppEvents.alertSuccess, ['Dashboard saved']);
|
|
|
|
const newUrl = locationUtil.stripBaseFromUrl(data.url);
|
|
const currentPath = this.$location.path();
|
|
|
|
if (newUrl !== currentPath) {
|
|
this.$location.url(newUrl).replace();
|
|
}
|
|
|
|
return this.dashboard;
|
|
}
|
|
|
|
save(clone: any, options?: DashboardSaveOptions) {
|
|
options.folderId = options.folderId >= 0 ? options.folderId : this.dashboard.meta.folderId || clone.folderId;
|
|
|
|
return promiseToDigest(this.$rootScope)(
|
|
backendSrv
|
|
.saveDashboard(clone, options)
|
|
.then((data: any) => this.postSave(data))
|
|
.catch(this.handleSaveDashboardError.bind(this, clone, { folderId: options.folderId }))
|
|
);
|
|
}
|
|
|
|
saveDashboard(
|
|
clone?: DashboardModel,
|
|
{ makeEditable = false, folderId, overwrite = false, message }: DashboardSaveOptions = {}
|
|
) {
|
|
if (clone) {
|
|
this.setCurrent(this.create(clone, this.dashboard.meta));
|
|
}
|
|
|
|
if (this.dashboard.meta.provisioned) {
|
|
return this.showDashboardProvisionedModal();
|
|
}
|
|
|
|
if (!(this.dashboard.meta.canSave || makeEditable)) {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
if (this.dashboard.title === 'New dashboard') {
|
|
return this.showSaveAsModal();
|
|
}
|
|
|
|
if (this.dashboard.version > 0) {
|
|
return this.showSaveModal();
|
|
}
|
|
|
|
return this.save(this.dashboard.getSaveModelClone(), { folderId, overwrite, message });
|
|
}
|
|
|
|
saveJSONDashboard(json: string) {
|
|
return this.save(JSON.parse(json), {});
|
|
}
|
|
|
|
showDashboardProvisionedModal() {
|
|
this.$rootScope.appEvent(CoreEvents.showModal, {
|
|
templateHtml: '<save-provisioned-dashboard-modal dismiss="dismiss()"></save-provisioned-dashboard-modal>',
|
|
});
|
|
}
|
|
|
|
showSaveAsModal() {
|
|
this.$rootScope.appEvent(CoreEvents.showModal, {
|
|
templateHtml: '<save-dashboard-as-modal dismiss="dismiss()"></save-dashboard-as-modal>',
|
|
modalClass: 'modal--narrow',
|
|
});
|
|
}
|
|
|
|
showSaveModal() {
|
|
this.$rootScope.appEvent(CoreEvents.showModal, {
|
|
templateHtml: '<save-dashboard-modal dismiss="dismiss()"></save-dashboard-modal>',
|
|
modalClass: 'modal--narrow',
|
|
});
|
|
}
|
|
|
|
starDashboard(dashboardId: string, isStarred: any) {
|
|
let promise;
|
|
|
|
if (isStarred) {
|
|
promise = promiseToDigest(this.$rootScope)(
|
|
backendSrv.delete('/api/user/stars/dashboard/' + dashboardId).then(() => {
|
|
return false;
|
|
})
|
|
);
|
|
} else {
|
|
promise = promiseToDigest(this.$rootScope)(
|
|
backendSrv.post('/api/user/stars/dashboard/' + dashboardId).then(() => {
|
|
return true;
|
|
})
|
|
);
|
|
}
|
|
|
|
return promise.then((res: boolean) => {
|
|
if (this.dashboard && this.dashboard.id === dashboardId) {
|
|
this.dashboard.meta.isStarred = res;
|
|
}
|
|
return res;
|
|
});
|
|
}
|
|
}
|
|
|
|
coreModule.service('dashboardSrv', DashboardSrv);
|
|
|
|
//
|
|
// Code below is to export the service to react components
|
|
//
|
|
|
|
let singletonInstance: DashboardSrv;
|
|
|
|
export function setDashboardSrv(instance: DashboardSrv) {
|
|
singletonInstance = instance;
|
|
}
|
|
|
|
export function getDashboardSrv(): DashboardSrv {
|
|
return singletonInstance;
|
|
}
|