Annotations: Migrate dashboardId to dashboardUID (#52588)

* Annotations: replaced dashboardId with dashboardUID when fetching annotations

* Annotations: Replaced dashboardID with dashboardUID when adding and updating annotations

* Annotations: Used dashboardUID for PanelChrome related methods

* Annotations: Used dashboarduid in annolistpanel

* Annotations: Used dashboardUID in graph plugin

* used boolean in canEditDashboard
This commit is contained in:
Leo 2022-08-01 09:21:41 +02:00 committed by GitHub
parent 046a2602ff
commit 1fc9f6f1c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 210 additions and 79 deletions

View File

@ -33,6 +33,7 @@ export interface AnnotationEvent {
id?: string; id?: string;
annotation?: any; annotation?: any;
dashboardId?: number; dashboardId?: number;
dashboardUID?: string;
panelId?: number; panelId?: number;
userId?: number; userId?: number;
login?: string; login?: string;

View File

@ -32,8 +32,8 @@ export interface PanelContext {
onToggleSeriesVisibility?: (label: string, mode: SeriesVisibilityChangeMode) => void; onToggleSeriesVisibility?: (label: string, mode: SeriesVisibilityChangeMode) => void;
canAddAnnotations?: () => boolean; canAddAnnotations?: () => boolean;
canEditAnnotations?: (dashboardId: number) => boolean; canEditAnnotations?: (dashboardUID?: string) => boolean;
canDeleteAnnotations?: (dashboardId: number) => boolean; canDeleteAnnotations?: (dashboardUID?: string) => boolean;
onAnnotationCreate?: (annotation: AnnotationEventUIModel) => void; onAnnotationCreate?: (annotation: AnnotationEventUIModel) => void;
onAnnotationUpdate?: (annotation: AnnotationEventUIModel) => void; onAnnotationUpdate?: (annotation: AnnotationEventUIModel) => void;
onAnnotationDelete?: (id: string) => void; onAnnotationDelete?: (id: string) => void;

View File

@ -125,6 +125,7 @@ const alertEventAndAnnotationFields: AnnotationFieldInfo[] = [
{ key: 'panelId' }, { key: 'panelId' },
{ key: 'alertId' }, { key: 'alertId' },
{ key: 'dashboardId' }, { key: 'dashboardId' },
{ key: 'dashboardUID' },
]; ];
export function getAnnotationsFromData( export function getAnnotationsFromData(

View File

@ -47,6 +47,9 @@ function setupTestContext(options: Partial<Props>) {
panelInitialized: jest.fn(), panelInitialized: jest.fn(),
getTimezone: () => 'browser', getTimezone: () => 'browser',
events: new EventBusSrv(), events: new EventBusSrv(),
canAddAnnotations: jest.fn(),
canEditAnnotations: jest.fn(),
canDeleteAnnotations: jest.fn(),
meta: { meta: {
isPublic: false, isPublic: false,
}, },

View File

@ -29,7 +29,6 @@ import { applyPanelTimeOverrides } from 'app/features/dashboard/utils/panel';
import { changeSeriesColorConfigFactory } from 'app/plugins/panel/timeseries/overrides/colorSeriesConfigFactory'; import { changeSeriesColorConfigFactory } from 'app/plugins/panel/timeseries/overrides/colorSeriesConfigFactory';
import { RenderEvent } from 'app/types/events'; import { RenderEvent } from 'app/types/events';
import { contextSrv } from '../../../core/services/context_srv';
import { isSoloRoute } from '../../../routes/utils'; import { isSoloRoute } from '../../../routes/utils';
import { deleteAnnotation, saveAnnotation, updateAnnotation } from '../../annotations/api'; import { deleteAnnotation, saveAnnotation, updateAnnotation } from '../../annotations/api';
import { getDashboardQueryRunner } from '../../query/state/DashboardQueryRunner/DashboardQueryRunner'; import { getDashboardQueryRunner } from '../../query/state/DashboardQueryRunner/DashboardQueryRunner';
@ -89,53 +88,16 @@ export class PanelChrome extends PureComponent<Props, State> {
onAnnotationCreate: this.onAnnotationCreate, onAnnotationCreate: this.onAnnotationCreate,
onAnnotationUpdate: this.onAnnotationUpdate, onAnnotationUpdate: this.onAnnotationUpdate,
onAnnotationDelete: this.onAnnotationDelete, onAnnotationDelete: this.onAnnotationDelete,
canAddAnnotations: this.canAddAnnotation,
onInstanceStateChange: this.onInstanceStateChange, onInstanceStateChange: this.onInstanceStateChange,
onToggleLegendSort: this.onToggleLegendSort, onToggleLegendSort: this.onToggleLegendSort,
canEditAnnotations: this.canEditAnnotation, canAddAnnotations: props.dashboard.canAddAnnotations.bind(props.dashboard),
canDeleteAnnotations: this.canDeleteAnnotation, canEditAnnotations: props.dashboard.canEditAnnotations.bind(props.dashboard),
canDeleteAnnotations: props.dashboard.canDeleteAnnotations.bind(props.dashboard),
}, },
data: this.getInitialPanelDataState(), data: this.getInitialPanelDataState(),
}; };
} }
canEditDashboard = () => Boolean(this.props.dashboard.meta.canEdit || this.props.dashboard.meta.canMakeEditable);
canAddAnnotation = () => {
let canAdd = true;
if (contextSrv.accessControlEnabled()) {
canAdd = !!this.props.dashboard.meta.annotationsPermissions?.dashboard.canAdd;
}
return canAdd && this.canEditDashboard();
};
canEditAnnotation = (dashboardId: number) => {
let canEdit = true;
if (contextSrv.accessControlEnabled()) {
if (dashboardId !== 0) {
canEdit = !!this.props.dashboard.meta.annotationsPermissions?.dashboard.canEdit;
} else {
canEdit = !!this.props.dashboard.meta.annotationsPermissions?.organization.canEdit;
}
}
return canEdit && this.canEditDashboard();
};
canDeleteAnnotation = (dashboardId: number) => {
let canDelete = true;
if (contextSrv.accessControlEnabled()) {
if (dashboardId !== 0) {
canDelete = !!this.props.dashboard.meta.annotationsPermissions?.dashboard.canDelete;
} else {
canDelete = !!this.props.dashboard.meta.annotationsPermissions?.organization.canDelete;
}
}
return canDelete && this.canEditDashboard();
};
// Due to a mutable panel model we get the sync settings via function that proactively reads from the model // Due to a mutable panel model we get the sync settings via function that proactively reads from the model
getSync = () => (this.props.isEditing ? DashboardCursorSync.Off : this.props.dashboard.graphTooltip); getSync = () => (this.props.isEditing ? DashboardCursorSync.Off : this.props.dashboard.graphTooltip);
@ -400,7 +362,7 @@ export class PanelChrome extends PureComponent<Props, State> {
onAnnotationCreate = async (event: AnnotationEventUIModel) => { onAnnotationCreate = async (event: AnnotationEventUIModel) => {
const isRegion = event.from !== event.to; const isRegion = event.from !== event.to;
const anno = { const anno = {
dashboardId: this.props.dashboard.id, dashboardUID: this.props.dashboard.uid,
panelId: this.props.panel.id, panelId: this.props.panel.id,
isRegion, isRegion,
time: event.from, time: event.from,
@ -423,7 +385,7 @@ export class PanelChrome extends PureComponent<Props, State> {
const isRegion = event.from !== event.to; const isRegion = event.from !== event.to;
const anno = { const anno = {
id: event.id, id: event.id,
dashboardId: this.props.dashboard.id, dashboardUID: this.props.dashboard.uid,
panelId: this.props.panel.id, panelId: this.props.panel.id,
isRegion, isRegion,
time: event.from, time: event.from,

View File

@ -1,5 +1,7 @@
import { keys as _keys } from 'lodash'; import { keys as _keys } from 'lodash';
import { contextSrv } from 'app/core/services/context_srv';
import { getDashboardModel } from '../../../../test/helpers/getDashboardModel'; import { getDashboardModel } from '../../../../test/helpers/getDashboardModel';
import { variableAdapters } from '../../variables/adapters'; import { variableAdapters } from '../../variables/adapters';
import { createAdHocVariableAdapter } from '../../variables/adhoc/adapter'; import { createAdHocVariableAdapter } from '../../variables/adhoc/adapter';
@ -9,7 +11,9 @@ import { setTimeSrv, TimeSrv } from '../services/TimeSrv';
import { DashboardModel } from '../state/DashboardModel'; import { DashboardModel } from '../state/DashboardModel';
import { PanelModel } from '../state/PanelModel'; import { PanelModel } from '../state/PanelModel';
jest.mock('app/core/services/context_srv', () => ({})); jest.mock('app/core/services/context_srv');
const mockContextSrv = jest.mocked(contextSrv, true);
variableAdapters.setInit(() => [ variableAdapters.setInit(() => [
createQueryVariableAdapter(), createQueryVariableAdapter(),
@ -860,20 +864,160 @@ describe('DashboardModel', () => {
describe('canAddAnnotations', () => { describe('canAddAnnotations', () => {
it.each` it.each`
canEdit | canMakeEditable | expected canEdit | canMakeEditable | canAdd | expected
${false} | ${false} | ${false} ${false} | ${true} | ${true} | ${true}
${false} | ${true} | ${true} ${true} | ${false} | ${true} | ${true}
${true} | ${false} | ${true} ${true} | ${true} | ${true} | ${true}
${true} | ${true} | ${true} ${false} | ${false} | ${true} | ${false}
${false} | ${true} | ${false} | ${false}
${true} | ${false} | ${false} | ${false}
${true} | ${true} | ${false} | ${false}
${false} | ${false} | ${false} | ${false}
`( `(
'when called with canEdit:{$canEdit}, canMakeEditable:{$canMakeEditable} and expected:{$expected}', 'when called with canEdit:{$canEdit}, canMakeEditable:{$canMakeEditable}, canAdd:{$canAdd} and expected:{$expected}',
({ canEdit, canMakeEditable, expected }) => { ({ canEdit, canMakeEditable, canAdd, expected }) => {
const dashboard = new DashboardModel({}); const dashboard = new DashboardModel(
{},
{
annotationsPermissions: {
dashboard: { canAdd, canEdit: true, canDelete: true },
organization: { canAdd: false, canEdit: false, canDelete: false },
},
}
);
dashboard.meta.canEdit = canEdit; dashboard.meta.canEdit = canEdit;
dashboard.meta.canMakeEditable = canMakeEditable; dashboard.meta.canMakeEditable = canMakeEditable;
mockContextSrv.accessControlEnabled.mockReturnValue(true);
const result = dashboard.canAddAnnotations();
expect(result).toBe(expected);
}
);
});
const result = dashboard.canEditDashboard(); describe('canEditAnnotations', () => {
it.each`
canEdit | canMakeEditable | canEditWithOrgPermission | expected
${false} | ${true} | ${true} | ${true}
${true} | ${false} | ${true} | ${true}
${true} | ${true} | ${true} | ${true}
${false} | ${false} | ${true} | ${false}
${false} | ${true} | ${false} | ${false}
${true} | ${false} | ${false} | ${false}
${true} | ${true} | ${false} | ${false}
${false} | ${false} | ${false} | ${false}
`(
'when called with canEdit:{$canEdit}, canMakeEditable:{$canMakeEditable}, canEditWithOrgPermission:{$canEditWithOrgPermission} and expected:{$expected}',
({ canEdit, canMakeEditable, canEditWithOrgPermission, expected }) => {
const dashboard = new DashboardModel(
{},
{
annotationsPermissions: {
dashboard: { canAdd: false, canEdit: false, canDelete: true },
organization: { canAdd: false, canEdit: canEditWithOrgPermission, canDelete: false },
},
}
);
dashboard.meta.canEdit = canEdit;
dashboard.meta.canMakeEditable = canMakeEditable;
mockContextSrv.accessControlEnabled.mockReturnValue(true);
const result = dashboard.canEditAnnotations();
expect(result).toBe(expected);
}
);
it.each`
canEdit | canMakeEditable | canEditWithDashboardPermission | expected
${false} | ${true} | ${true} | ${true}
${true} | ${false} | ${true} | ${true}
${true} | ${true} | ${true} | ${true}
${false} | ${false} | ${true} | ${false}
${false} | ${true} | ${false} | ${false}
${true} | ${false} | ${false} | ${false}
${true} | ${true} | ${false} | ${false}
${false} | ${false} | ${false} | ${false}
`(
'when called with canEdit:{$canEdit}, canMakeEditable:{$canMakeEditable}, canEditWithDashboardPermission:{$canEditWithDashboardPermission} and expected:{$expected}',
({ canEdit, canMakeEditable, canEditWithDashboardPermission, expected }) => {
const dashboard = new DashboardModel(
{},
{
annotationsPermissions: {
dashboard: { canAdd: false, canEdit: canEditWithDashboardPermission, canDelete: true },
organization: { canAdd: false, canEdit: false, canDelete: false },
},
}
);
dashboard.meta.canEdit = canEdit;
dashboard.meta.canMakeEditable = canMakeEditable;
mockContextSrv.accessControlEnabled.mockReturnValue(true);
const result = dashboard.canEditAnnotations('testDashboardUID');
expect(result).toBe(expected);
}
);
});
describe('canDeleteAnnotations', () => {
it.each`
canEdit | canMakeEditable | canDeleteWithOrgPermission | expected
${false} | ${true} | ${true} | ${true}
${true} | ${false} | ${true} | ${true}
${true} | ${true} | ${true} | ${true}
${false} | ${false} | ${true} | ${false}
${false} | ${true} | ${false} | ${false}
${true} | ${false} | ${false} | ${false}
${true} | ${true} | ${false} | ${false}
${false} | ${false} | ${false} | ${false}
`(
'when called with canEdit:{$canEdit}, canMakeEditable:{$canMakeEditable}, canDeleteWithOrgPermission:{$canDeleteWithOrgPermission} and expected:{$expected}',
({ canEdit, canMakeEditable, canDeleteWithOrgPermission, expected }) => {
const dashboard = new DashboardModel(
{},
{
annotationsPermissions: {
dashboard: { canAdd: false, canEdit: false, canDelete: false },
organization: { canAdd: false, canEdit: false, canDelete: canDeleteWithOrgPermission },
},
}
);
dashboard.meta.canEdit = canEdit;
dashboard.meta.canMakeEditable = canMakeEditable;
mockContextSrv.accessControlEnabled.mockReturnValue(true);
const result = dashboard.canDeleteAnnotations();
expect(result).toBe(expected);
}
);
it.each`
canEdit | canMakeEditable | canDeleteWithDashboardPermission | expected
${false} | ${true} | ${true} | ${true}
${true} | ${false} | ${true} | ${true}
${true} | ${true} | ${true} | ${true}
${false} | ${false} | ${true} | ${false}
${false} | ${true} | ${false} | ${false}
${true} | ${false} | ${false} | ${false}
${true} | ${true} | ${false} | ${false}
${false} | ${false} | ${false} | ${false}
`(
'when called with canEdit:{$canEdit}, canMakeEditable:{$canMakeEditable}, canDeleteWithDashboardPermission:{$canDeleteWithDashboardPermission} and expected:{$expected}',
({ canEdit, canMakeEditable, canDeleteWithDashboardPermission, expected }) => {
const dashboard = new DashboardModel(
{},
{
annotationsPermissions: {
dashboard: { canAdd: false, canEdit: false, canDelete: canDeleteWithDashboardPermission },
organization: { canAdd: false, canEdit: false, canDelete: false },
},
}
);
dashboard.meta.canEdit = canEdit;
dashboard.meta.canMakeEditable = canMakeEditable;
mockContextSrv.accessControlEnabled.mockReturnValue(true);
const result = dashboard.canDeleteAnnotations('testDashboardUID');
expect(result).toBe(expected); expect(result).toBe(expected);
} }
); );

View File

@ -1073,12 +1073,13 @@ export class DashboardModel implements TimeModel {
return this.getVariablesFromState(this.uid); return this.getVariablesFromState(this.uid);
} }
canEditAnnotations(dashboardId: number) { canEditAnnotations(dashboardUID?: string) {
let canEdit = true; let canEdit = true;
// if RBAC is enabled there are additional conditions to check // if RBAC is enabled there are additional conditions to check
if (contextSrv.accessControlEnabled()) { if (contextSrv.accessControlEnabled()) {
if (dashboardId === 0) { // dashboardUID is falsy when it is an organizational annotation
if (!dashboardUID) {
canEdit = !!this.meta.annotationsPermissions?.organization.canEdit; canEdit = !!this.meta.annotationsPermissions?.organization.canEdit;
} else { } else {
canEdit = !!this.meta.annotationsPermissions?.dashboard.canEdit; canEdit = !!this.meta.annotationsPermissions?.dashboard.canEdit;
@ -1087,14 +1088,29 @@ export class DashboardModel implements TimeModel {
return this.canEditDashboard() && canEdit; return this.canEditDashboard() && canEdit;
} }
canDeleteAnnotations(dashboardUID?: string) {
let canDelete = true;
if (contextSrv.accessControlEnabled()) {
// dashboardUID is falsy when it is an organizational annotation
if (!dashboardUID) {
canDelete = !!this.meta.annotationsPermissions?.organization.canDelete;
} else {
canDelete = !!this.meta.annotationsPermissions?.dashboard.canDelete;
}
}
return canDelete && this.canEditDashboard();
}
canAddAnnotations() { canAddAnnotations() {
// If RBAC is enabled there are additional conditions to check // If RBAC is enabled there are additional conditions to check.
const canAdd = !contextSrv.accessControlEnabled() || this.meta.annotationsPermissions?.dashboard.canAdd; const canAdd = !contextSrv.accessControlEnabled() || Boolean(this.meta.annotationsPermissions?.dashboard.canAdd);
return this.canEditDashboard() && canAdd; return this.canEditDashboard() && canAdd;
} }
canEditDashboard() { canEditDashboard() {
return this.meta.canEdit || this.meta.canMakeEditable; return Boolean(this.meta.canEdit || this.meta.canMakeEditable);
} }
shouldUpdateDashboardPanelFromJSON(updatedPanel: PanelModel, panel: PanelModel) { shouldUpdateDashboardPanelFromJSON(updatedPanel: PanelModel, panel: PanelModel) {

View File

@ -64,7 +64,7 @@ describe('grafana data source', () => {
type: GrafanaAnnotationType.Dashboard, type: GrafanaAnnotationType.Dashboard,
tags: ['tag1'], tags: ['tag1'],
}, },
{ id: 1 } { uid: 'DSNdW0gVk' }
); );
beforeEach(() => { beforeEach(() => {
@ -78,7 +78,7 @@ describe('grafana data source', () => {
}); });
}); });
function setupAnnotationQueryOptions(annotation: Partial<GrafanaAnnotationQuery>, dashboard?: { id: number }) { function setupAnnotationQueryOptions(annotation: Partial<GrafanaAnnotationQuery>, dashboard?: { uid: string }) {
return { return {
annotation: { annotation: {
target: annotation, target: annotation,

View File

@ -170,11 +170,11 @@ export class GrafanaDatasource extends DataSourceWithBackend<GrafanaQuery> {
if (target.type === GrafanaAnnotationType.Dashboard) { if (target.type === GrafanaAnnotationType.Dashboard) {
// if no dashboard id yet return // if no dashboard id yet return
if (!options.dashboard.id) { if (!options.dashboard.uid) {
return Promise.resolve({ data: [] }); return Promise.resolve({ data: [] });
} }
// filter by dashboard id // filter by dashboard id
params.dashboardId = options.dashboard.id; params.dashboardUID = options.dashboard.uid;
// remove tags filter if any // remove tags filter if any
delete params.tags; delete params.tags;
} else { } else {
@ -202,7 +202,7 @@ export class GrafanaDatasource extends DataSourceWithBackend<GrafanaQuery> {
const annotations = await getBackendSrv().get( const annotations = await getBackendSrv().get(
'/api/annotations', '/api/annotations',
params, params,
`grafana-data-source-annotations-${annotation.name}-${options.dashboard?.id}` `grafana-data-source-annotations-${annotation.name}-${options.dashboard?.uid}`
); );
return { data: [toDataFrame(annotations)] }; return { data: [toDataFrame(annotations)] };
} }

View File

@ -41,6 +41,8 @@ const defaultResult: any = {
panelId: 13, panelId: 13,
dashboardId: 14, // deliberately different from panelId dashboardId: 14, // deliberately different from panelId
id: 14, id: 14,
uid: '7MeksYbmk',
dashboardUID: '7MeksYbmk',
url: '/d/asdkjhajksd/some-dash', url: '/d/asdkjhajksd/some-dash',
}; };
@ -53,7 +55,7 @@ async function setupTestContext({
const getMock = jest.spyOn(backendSrv, 'get'); const getMock = jest.spyOn(backendSrv, 'get');
getMock.mockResolvedValue(results); getMock.mockResolvedValue(results);
const dash: any = { id: 1, formatDate: (time: number) => new Date(time).toISOString() }; const dash: any = { uid: 'srx16xR4z', formatDate: (time: number) => new Date(time).toISOString() };
const dashSrv: any = { getCurrent: () => dash }; const dashSrv: any = { getCurrent: () => dash };
setDashboardSrv(dashSrv); setDashboardSrv(dashSrv);
const pushSpy = jest.spyOn(locationService, 'push'); const pushSpy = jest.spyOn(locationService, 'push');
@ -99,7 +101,7 @@ describe('AnnoListPanel', () => {
expect(getMock).toHaveBeenCalledWith( expect(getMock).toHaveBeenCalledWith(
'/api/annotations', '/api/annotations',
{ {
dashboardId: 1, dashboardUID: 'srx16xR4z',
limit: 10, limit: 10,
tags: ['tag A', 'tag B'], tags: ['tag A', 'tag B'],
type: 'annotation', type: 'annotation',
@ -205,7 +207,7 @@ describe('AnnoListPanel', () => {
await userEvent.click(screen.getByText(/result text/i)); await userEvent.click(screen.getByText(/result text/i));
await waitFor(() => expect(getMock).toHaveBeenCalledTimes(1)); await waitFor(() => expect(getMock).toHaveBeenCalledTimes(1));
expect(getMock).toHaveBeenCalledWith('/api/search', { dashboardIds: 14 }); expect(getMock).toHaveBeenCalledWith('/api/search', { dashboardUIDs: '7MeksYbmk' });
expect(pushSpy).toHaveBeenCalledTimes(1); expect(pushSpy).toHaveBeenCalledTimes(1);
expect(pushSpy).toHaveBeenCalledWith('/d/asdkjhajksd/some-dash?from=1609458600000&to=1609459800000'); expect(pushSpy).toHaveBeenCalledWith('/d/asdkjhajksd/some-dash?from=1609458600000&to=1609459800000');
}); });
@ -223,7 +225,7 @@ describe('AnnoListPanel', () => {
expect(getMock).toHaveBeenCalledWith( expect(getMock).toHaveBeenCalledWith(
'/api/annotations', '/api/annotations',
{ {
dashboardId: 1, dashboardUID: 'srx16xR4z',
limit: 10, limit: 10,
tags: ['tag A', 'tag B', 'Result tag B'], tags: ['tag A', 'tag B', 'Result tag B'],
type: 'annotation', type: 'annotation',
@ -247,7 +249,7 @@ describe('AnnoListPanel', () => {
expect(getMock).toHaveBeenCalledWith( expect(getMock).toHaveBeenCalledWith(
'/api/annotations', '/api/annotations',
{ {
dashboardId: 1, dashboardUID: 'srx16xR4z',
limit: 10, limit: 10,
tags: ['tag A', 'tag B'], tags: ['tag A', 'tag B'],
type: 'annotation', type: 'annotation',

View File

@ -97,7 +97,7 @@ export class AnnoListPanel extends PureComponent<Props, State> {
}; };
if (options.onlyFromThisDashboard) { if (options.onlyFromThisDashboard) {
params.dashboardId = getDashboardSrv().getCurrent()?.id; params.dashboardUID = getDashboardSrv().getCurrent()?.uid;
} }
let timeInfo = ''; let timeInfo = '';
@ -148,13 +148,13 @@ export class AnnoListPanel extends PureComponent<Props, State> {
params.viewPanel = anno.panelId; params.viewPanel = anno.panelId;
} }
if (current?.id === anno.dashboardId) { if (current?.uid === anno.dashboardUID) {
locationService.partial(params); locationService.partial(params);
return; return;
} }
const result = await getBackendSrv().get('/api/search', { dashboardIds: anno.dashboardId }); const result = await getBackendSrv().get('/api/search', { dashboardUIDs: anno.dashboardUID });
if (result && result.length && result[0].id === anno.dashboardId) { if (result && result.length && result[0].uid === anno.dashboardUID) {
const dash = result[0]; const dash = result[0];
const url = new URL(dash.url, window.location.origin); const url = new URL(dash.url, window.location.origin);
url.searchParams.set('from', params.from); url.searchParams.set('from', params.from);
@ -162,7 +162,7 @@ export class AnnoListPanel extends PureComponent<Props, State> {
locationService.push(locationUtil.stripBaseFromUrl(url.toString())); locationService.push(locationUtil.stripBaseFromUrl(url.toString()));
return; return;
} }
appEvents.emit(AppEvents.alertWarning, ['Unknown Dashboard: ' + anno.dashboardId]); appEvents.emit(AppEvents.alertWarning, ['Unknown Dashboard: ' + anno.dashboardUID]);
}; };
_timeOffset(time: number, offset: string, subtract = false): number { _timeOffset(time: number, offset: string, subtract = false): number {

View File

@ -23,7 +23,7 @@ export class EventEditorCtrl {
$onInit() { $onInit() {
this.event.panelId = this.panelCtrl.panel.id; // set correct id if in panel edit this.event.panelId = this.panelCtrl.panel.id; // set correct id if in panel edit
this.event.dashboardId = this.panelCtrl.dashboard.id; this.event.dashboardUID = this.panelCtrl.dashboard.uid;
// Annotations query returns time as Unix timestamp in milliseconds // Annotations query returns time as Unix timestamp in milliseconds
this.event.time = tryEpochToMoment(this.event.time); this.event.time = tryEpochToMoment(this.event.time);

View File

@ -31,7 +31,7 @@ export class EventManager {
updateTime(range: { from: any; to: any }) { updateTime(range: { from: any; to: any }) {
if (!this.event) { if (!this.event) {
this.event = {}; this.event = {};
this.event.dashboardId = this.panelCtrl.dashboard.id; this.event.dashboardUID = this.panelCtrl.dashboard.uid;
this.event.panelId = this.panelCtrl.panel.id; this.event.panelId = this.panelCtrl.panel.id;
} }

View File

@ -95,8 +95,8 @@ export function AnnotationMarker({ annotation, timeZone, width }: Props) {
timeFormatter={timeFormatter} timeFormatter={timeFormatter}
onEdit={onAnnotationEdit} onEdit={onAnnotationEdit}
onDelete={onAnnotationDelete} onDelete={onAnnotationDelete}
canEdit={canEditAnnotations!(annotation.dashboardId)} canEdit={canEditAnnotations!(annotation.dashboardUID)}
canDelete={canDeleteAnnotations!(annotation.dashboardId)} canDelete={canDeleteAnnotations!(annotation.dashboardUID)}
/> />
); );
}, [canEditAnnotations, canDeleteAnnotations, onAnnotationDelete, onAnnotationEdit, timeFormatter, annotation]); }, [canEditAnnotations, canDeleteAnnotations, onAnnotationDelete, onAnnotationEdit, timeFormatter, annotation]);

View File

@ -1,6 +1,8 @@
interface AnnotationsDataFrameViewDTO { interface AnnotationsDataFrameViewDTO {
id: string; id: string;
/** @deprecate */
dashboardId: number; dashboardId: number;
dashboardUID: string;
time: number; time: number;
timeEnd: number; timeEnd: number;
text: string; text: string;