Annotations: Add frontend api wrapper (#111452)

This commit is contained in:
Ryan McKinley
2025-09-23 16:08:24 +03:00
committed by GitHub
parent c16c764c6f
commit 9887a4184d
8 changed files with 69 additions and 36 deletions
@@ -10,6 +10,10 @@ const get = jest.fn(() => {
});
jest.mock('@grafana/runtime', () => ({
config: {
namespace: 'default',
featureToggles: {},
},
getBackendSrv: () => ({
get,
}),
@@ -1,11 +1,9 @@
import { getBackendSrv } from '@grafana/runtime';
import { annotationServer } from 'app/features/annotations/api';
import { StateHistoryItem } from 'app/types/unified-alerting';
export function fetchAnnotations(alertUID: string): Promise<StateHistoryItem[]> {
return getBackendSrv()
.get('/api/annotations', {
alertUID,
})
return annotationServer()
.forAlert(alertUID)
.then((result) => {
return result?.sort(sortStateHistory);
});
+47 -14
View File
@@ -1,24 +1,57 @@
import { AnnotationEvent } from '@grafana/data';
import { AnnotationEvent, DataFrame, toDataFrame } from '@grafana/data';
import { getBackendSrv } from '@grafana/runtime';
import { StateHistoryItem } from 'app/types/unified-alerting';
import { AnnotationTagsResponse } from './types';
export function saveAnnotation(annotation: AnnotationEvent) {
return getBackendSrv().post('/api/annotations', annotation);
export interface AnnotationServer {
query(params: Record<string, unknown>, requestId: string): Promise<DataFrame>;
forAlert(alertUID: string): Promise<StateHistoryItem[]>;
save(annotation: AnnotationEvent): Promise<AnnotationEvent>;
update(annotation: AnnotationEvent): Promise<unknown>;
delete(annotation: AnnotationEvent): Promise<unknown>;
tags(): Promise<Array<{ term: string; count: number }>>;
}
export function updateAnnotation(annotation: AnnotationEvent) {
return getBackendSrv().put(`/api/annotations/${annotation.id}`, annotation);
class LegacyAnnotationServer implements AnnotationServer {
query(params: unknown, requestId: string): Promise<DataFrame> {
return getBackendSrv()
.get('/api/annotations', params, requestId)
.then((v) => toDataFrame(v));
}
forAlert(alertUID: string) {
return getBackendSrv().get('/api/annotations', {
alertUID,
});
}
save(annotation: AnnotationEvent) {
return getBackendSrv().post('/api/annotations', annotation);
}
update(annotation: AnnotationEvent) {
return getBackendSrv().put(`/api/annotations/${annotation.id}`, annotation);
}
delete(annotation: AnnotationEvent) {
return getBackendSrv().delete(`/api/annotations/${annotation.id}`);
}
async tags() {
const response = await getBackendSrv().get<AnnotationTagsResponse>('/api/annotations/tags');
return response.result.tags.map(({ tag, count }) => ({
term: tag,
count,
}));
}
}
export function deleteAnnotation(annotation: AnnotationEvent) {
return getBackendSrv().delete(`/api/annotations/${annotation.id}`);
}
let instance: AnnotationServer | null = null;
export async function getAnnotationTags() {
const response: AnnotationTagsResponse = await getBackendSrv().get('/api/annotations/tags');
return response.result.tags.map(({ tag, count }) => ({
term: tag,
count,
}));
export function annotationServer(): AnnotationServer {
if (!instance) {
instance = new LegacyAnnotationServer();
}
return instance;
}
@@ -3,7 +3,7 @@ import { config, getDataSourceSrv } from '@grafana/runtime';
import { AdHocFiltersVariable, dataLayers, sceneGraph, sceneUtils, VizPanel } from '@grafana/scenes';
import { DataSourceRef } from '@grafana/schema';
import { AdHocFilterItem, PanelContext } from '@grafana/ui';
import { deleteAnnotation, saveAnnotation, updateAnnotation } from 'app/features/annotations/api';
import { annotationServer } from 'app/features/annotations/api';
import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
import { getDashboardSceneFor, getPanelIdForVizPanel, getQueryRunnerFor } from '../utils/utils';
@@ -84,7 +84,7 @@ export function setDashboardPanelContext(vizPanel: VizPanel, context: PanelConte
text: event.description,
};
await saveAnnotation(anno);
await annotationServer().save(anno);
reRunBuiltInAnnotationsLayer(dashboard);
@@ -106,7 +106,7 @@ export function setDashboardPanelContext(vizPanel: VizPanel, context: PanelConte
text: event.description,
};
await updateAnnotation(anno);
await annotationServer().update(anno);
reRunBuiltInAnnotationsLayer(dashboard);
@@ -114,7 +114,7 @@ export function setDashboardPanelContext(vizPanel: VizPanel, context: PanelConte
};
context.onAnnotationDelete = async (id: string) => {
await deleteAnnotation({ id });
await annotationServer().delete({ id });
reRunBuiltInAnnotationsLayer(getDashboardSceneFor(vizPanel));
@@ -35,6 +35,7 @@ import {
import appEvents from 'app/core/app_events';
import config from 'app/core/config';
import { profiler } from 'app/core/profiler';
import { annotationServer } from 'app/features/annotations/api';
import { applyPanelTimeOverrides } from 'app/features/dashboard/utils/panel';
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { applyFilterFromTable } from 'app/features/variables/adhoc/actions';
@@ -43,7 +44,6 @@ import { changeSeriesColorConfigFactory } from 'app/plugins/panel/timeseries/ove
import { dispatch } from 'app/store/store';
import { RenderEvent } from 'app/types/events';
import { deleteAnnotation, saveAnnotation, updateAnnotation } from '../../annotations/api';
import { getDashboardQueryRunner } from '../../query/state/DashboardQueryRunner/DashboardQueryRunner';
import { getTimeSrv, TimeSrv } from '../services/TimeSrv';
import { DashboardModel } from '../state/DashboardModel';
@@ -421,13 +421,13 @@ export class PanelStateWrapper extends PureComponent<Props, State> {
tags: event.tags,
text: event.description,
};
await saveAnnotation(anno);
await annotationServer().save(anno);
getDashboardQueryRunner().run({ dashboard: this.props.dashboard, range: this.timeSrv.timeRange() });
this.state.context.eventBus.publish(new AnnotationChangeEvent(anno));
};
onAnnotationDelete = async (id: string) => {
await deleteAnnotation({ id });
await annotationServer().delete({ id });
getDashboardQueryRunner().run({ dashboard: this.props.dashboard, range: this.timeSrv.timeRange() });
this.state.context.eventBus.publish(new AnnotationChangeEvent({ id }));
};
@@ -444,7 +444,7 @@ export class PanelStateWrapper extends PureComponent<Props, State> {
tags: event.tags,
text: event.description,
};
await updateAnnotation(anno);
await annotationServer().update(anno);
getDashboardQueryRunner().run({ dashboard: this.props.dashboard, range: this.timeSrv.timeRange() });
this.state.context.eventBus.publish(new AnnotationChangeEvent(anno));
@@ -6,7 +6,7 @@ import { GrafanaTheme2, SelectableValue } from '@grafana/data';
import { Field, FieldSet, Select, Switch, useStyles2 } from '@grafana/ui';
import { TagFilter } from 'app/core/components/TagFilter/TagFilter';
import { TimeRegionConfig } from 'app/core/utils/timeRegions';
import { getAnnotationTags } from 'app/features/annotations/api';
import { annotationServer } from 'app/features/annotations/api';
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
import { GrafanaAnnotationQuery, GrafanaAnnotationType, GrafanaQuery, GrafanaQueryType } from '../types';
@@ -146,7 +146,7 @@ export default function AnnotationQueryEditor({ query, onChange }: Props) {
allowCustomValue
inputId="grafana-annotations__tags"
onChange={onTagsChange}
tagOptions={getAnnotationTags}
tagOptions={annotationServer().tags}
tags={tags ?? []}
/>
</Field>
@@ -13,19 +13,18 @@ import {
isValidLiveChannelAddress,
MutableDataFrame,
parseLiveChannelAddress,
toDataFrame,
dataFrameFromJSON,
LoadingState,
} from '@grafana/data';
import {
DataSourceWithBackend,
getBackendSrv,
getDataSourceSrv,
getGrafanaLiveSrv,
getTemplateSrv,
StreamingFrameOptions,
} from '@grafana/runtime';
import { DataSourceRef } from '@grafana/schema';
import { annotationServer } from 'app/features/annotations/api';
import { migrateDatasourceNameToRef } from 'app/features/dashboard/state/DashboardMigrator';
import { getDashboardSrv } from '../../../features/dashboard/services/DashboardSrv';
@@ -241,12 +240,11 @@ export class GrafanaDatasource extends DataSourceWithBackend<GrafanaQuery> {
params.tags = tags;
}
const annotations = await getBackendSrv().get(
'/api/annotations',
const df = await annotationServer().query(
params,
`grafana-data-source-annotations-${annotation.name}-${options.dashboard?.uid}`
);
return { data: [toDataFrame(annotations)] };
return { data: [df] };
}
testDatasource(): Promise<TestDataSourceResponse> {
@@ -8,7 +8,7 @@ import { Trans, t } from '@grafana/i18n';
import { Button, Field, Stack, TextArea, usePanelContext, useStyles2 } from '@grafana/ui';
import { Form } from 'app/core/components/Form/Form';
import { TagFilter } from 'app/core/components/TagFilter/TagFilter';
import { getAnnotationTags } from 'app/features/annotations/api';
import { annotationServer } from 'app/features/annotations/api';
interface Props {
annoVals: Record<string, any[]>;
@@ -109,7 +109,7 @@ export const AnnotationEditor2 = ({ annoVals, annoIdx, dismiss, timeZone, ...oth
allowCustomValue
placeholder={t('timeseries.annotation-editor2.placeholder-add-tags', 'Add tags')}
onChange={onChange}
tagOptions={getAnnotationTags}
tagOptions={annotationServer().tags}
tags={field.value}
/>
);