Data layers: Integrate latest scenes changes (#84804)

* bump scenes

* Improved data layers

* DashboardScene: Allow transformations by topic

* Use stable version

* Mock grafana ds to avoid anno layers test failure

* Fix name change

---------

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
Dominik Prokop 2024-03-21 18:23:47 +01:00 committed by GitHub
parent 1ba4d525a2
commit f74d5ff93e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 106 additions and 74 deletions

View File

@ -252,7 +252,7 @@
"@grafana/prometheus": "workspace:*",
"@grafana/runtime": "workspace:*",
"@grafana/saga-icons": "workspace:*",
"@grafana/scenes": "3.13.3",
"@grafana/scenes": "4.0.1",
"@grafana/schema": "workspace:*",
"@grafana/sql": "workspace:*",
"@grafana/ui": "workspace:*",

View File

@ -95,6 +95,17 @@ const instance2SettingsMock = {
},
};
// Mocking the build in Grafana data source to avoid annotations data layer errors.
const grafanaDs = {
id: 1,
uid: '-- Grafana --',
name: 'grafana',
type: 'grafana',
meta: {
id: 'grafana',
},
};
// Mock the store module
jest.mock('app/core/store', () => ({
exists: jest.fn(),
@ -112,6 +123,11 @@ jest.mock('@grafana/runtime', () => ({
},
getDataSourceSrv: () => ({
get: async (ref: DataSourceRef) => {
// Mocking the build in Grafana data source to avoid annotations data layer errors.
if (ref.uid === '-- Grafana --') {
return grafanaDs;
}
if (ref.uid === 'gdev-testdata') {
return ds1Mock;
}
@ -707,6 +723,7 @@ describe('VizPanelManager', () => {
const setupTest = (panelId: string) => {
const scene = transformSaveModelToScene({ dashboard: testDashboard, meta: {} });
const panel = findVizPanelByKey(scene, panelId)!;
const vizPanelManager = VizPanelManager.createFor(panel);

View File

@ -1,7 +1,7 @@
import { Unsubscribable } from 'rxjs';
import {
SceneDataLayers,
SceneDataLayerSet,
SceneGridLayout,
SceneObjectStateChangedEvent,
SceneRefreshPicker,
@ -38,7 +38,7 @@ export class DashboardSceneChangeTracker {
if (payload.changedObject instanceof behaviors.CursorSync) {
this.detectChanges();
}
if (payload.changedObject instanceof SceneDataLayers) {
if (payload.changedObject instanceof SceneDataLayerSet) {
this.detectChanges();
}
if (payload.changedObject instanceof DashboardGridItem) {

View File

@ -122,30 +122,33 @@ export class AlertStatesDataLayer
this.querySub = alerStatesExecution.subscribe({
next: (stateUpdate) => {
this.publishResults(
{
state: LoadingState.Done,
series: [toDataFrame(stateUpdate)],
timeRange: timeRange.state.value,
},
DataTopic.AlertStates
);
const frame = toDataFrame(stateUpdate);
this.publishResults({
state: LoadingState.Done,
series: [
{
...frame,
meta: {
...frame.meta,
dataTopic: DataTopic.AlertStates,
},
},
],
timeRange: timeRange.state.value,
});
},
error: (err) => {
this.handleError(err);
this.publishResults(
{
state: LoadingState.Error,
series: [],
errors: [
{
message: getMessageFromError(err),
},
],
timeRange: timeRange.state.value,
},
DataTopic.AlertStates
);
this.publishResults({
state: LoadingState.Error,
series: [],
errors: [
{
message: getMessageFromError(err),
},
],
timeRange: timeRange.state.value,
});
},
});
}

View File

@ -1,5 +1,5 @@
import { AnnotationChangeEvent, AnnotationEventUIModel, CoreApp, DataFrame } from '@grafana/data';
import { AdHocFiltersVariable, dataLayers, SceneDataLayers, sceneGraph, sceneUtils, VizPanel } from '@grafana/scenes';
import { AdHocFiltersVariable, dataLayers, SceneDataLayerSet, 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';
@ -129,7 +129,7 @@ export function setDashboardPanelContext(vizPanel: VizPanel, context: PanelConte
function getBuiltInAnnotationsLayer(scene: DashboardScene): dataLayers.AnnotationsDataLayer | undefined {
// When there is no builtin annotations query we disable the ability to add annotations
if (scene.state.$data instanceof SceneDataLayers) {
if (scene.state.$data instanceof SceneDataLayerSet) {
for (const layer of scene.state.$data.state.layers) {
if (layer instanceof dataLayers.AnnotationsDataLayer) {
if (layer.state.isEnabled && layer.state.query.builtIn) {

View File

@ -20,7 +20,7 @@ import {
GroupByVariable,
QueryVariable,
SceneDataLayerControls,
SceneDataLayers,
SceneDataLayerSet,
SceneDataTransformer,
SceneGridLayout,
SceneGridRow,
@ -1230,10 +1230,10 @@ describe('transformSaveModelToScene', () => {
it('Should build correct scene model', () => {
const scene = transformSaveModelToScene({ dashboard: dashboard_to_load1 as any, meta: {} });
expect(scene.state.$data).toBeInstanceOf(SceneDataLayers);
expect(scene.state.$data).toBeInstanceOf(SceneDataLayerSet);
expect(scene.state.controls!.state.variableControls[1]).toBeInstanceOf(SceneDataLayerControls);
const dataLayers = scene.state.$data as SceneDataLayers;
const dataLayers = scene.state.$data as SceneDataLayerSet;
expect(dataLayers.state.layers).toHaveLength(4);
expect(dataLayers.state.layers[0].state.name).toBe('Annotations & Alerts');
expect(dataLayers.state.layers[0].state.isEnabled).toBe(true);
@ -1258,10 +1258,10 @@ describe('transformSaveModelToScene', () => {
config.unifiedAlertingEnabled = true;
const scene = transformSaveModelToScene({ dashboard: dashboard_to_load1 as any, meta: {} });
expect(scene.state.$data).toBeInstanceOf(SceneDataLayers);
expect(scene.state.$data).toBeInstanceOf(SceneDataLayerSet);
expect(scene.state.controls!.state.variableControls[1]).toBeInstanceOf(SceneDataLayerControls);
const dataLayers = scene.state.$data as SceneDataLayers;
const dataLayers = scene.state.$data as SceneDataLayerSet;
expect(dataLayers.state.layers).toHaveLength(5);
expect(dataLayers.state.layers[4].state.name).toBe('Alert States');
});
@ -1272,10 +1272,10 @@ describe('transformSaveModelToScene', () => {
dashboard.panels![0].alert = {};
const scene = transformSaveModelToScene({ dashboard: dashboard_to_load1 as any, meta: {} });
expect(scene.state.$data).toBeInstanceOf(SceneDataLayers);
expect(scene.state.$data).toBeInstanceOf(SceneDataLayerSet);
expect(scene.state.controls!.state.variableControls[1]).toBeInstanceOf(SceneDataLayerControls);
const dataLayers = scene.state.$data as SceneDataLayers;
const dataLayers = scene.state.$data as SceneDataLayerSet;
expect(dataLayers.state.layers).toHaveLength(5);
expect(dataLayers.state.layers[4].state.name).toBe('Alert States');
});

View File

@ -20,7 +20,7 @@ import {
behaviors,
VizPanelState,
SceneGridItemLike,
SceneDataLayers,
SceneDataLayerSet,
SceneDataLayerProvider,
SceneDataLayerControls,
TextBoxVariable,
@ -309,7 +309,7 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel)
],
$data:
layers.length > 0
? new SceneDataLayers({
? new SceneDataLayerSet({
layers,
})
: undefined,

View File

@ -16,7 +16,7 @@ import {
} from '@grafana/data';
import { getPanelPlugin } from '@grafana/data/test/__mocks__/pluginMocks';
import { getPluginLinkExtensions, setPluginImportUtils } from '@grafana/runtime';
import { MultiValueVariable, SceneDataLayers, SceneGridLayout, SceneGridRow, VizPanel } from '@grafana/scenes';
import { MultiValueVariable, SceneDataLayerSet, SceneGridLayout, SceneGridRow, VizPanel } from '@grafana/scenes';
import { Dashboard, LoadingState, Panel, RowPanel, VariableRefresh } from '@grafana/schema';
import { PanelModel } from 'app/features/dashboard/state';
import { getTimeRange } from 'app/features/dashboard/utils/timeRange';
@ -405,7 +405,7 @@ describe('transformSceneToSaveModel', () => {
it('should transform annotations to save model after state changes', () => {
const scene = transformSaveModelToScene({ dashboard: dashboard_to_load1 as any, meta: {} });
const layers = (scene.state.$data as SceneDataLayers)?.state.layers;
const layers = (scene.state.$data as SceneDataLayerSet)?.state.layers;
const enabledLayer = layers[1];
const hiddenLayer = layers[3];

View File

@ -3,7 +3,6 @@ import { isEqual } from 'lodash';
import { isEmptyObject, ScopedVars, TimeRange } from '@grafana/data';
import {
behaviors,
SceneDataLayers,
SceneGridItemLike,
SceneGridLayout,
SceneGridRow,
@ -11,6 +10,7 @@ import {
SceneDataTransformer,
SceneVariableSet,
LocalValueVariable,
SceneDataLayerSet,
} from '@grafana/scenes';
import {
AnnotationQuery,
@ -77,7 +77,7 @@ export function transformSceneToSaveModel(scene: DashboardScene, isSnapshot = fa
}
let annotations: AnnotationQuery[] = [];
if (data instanceof SceneDataLayers) {
if (data instanceof SceneDataLayerSet) {
const layers = data.state.layers;
annotations = dataLayersToAnnotations(layers);

View File

@ -1,7 +1,7 @@
import { map, of } from 'rxjs';
import { AnnotationQuery, DataQueryRequest, DataSourceApi, LoadingState, PanelData } from '@grafana/data';
import { SceneDataLayers, SceneGridLayout, SceneTimeRange, dataLayers } from '@grafana/scenes';
import { SceneDataLayerSet, SceneGridLayout, SceneTimeRange, dataLayers } from '@grafana/scenes';
import { AlertStatesDataLayer } from '../scene/AlertStatesDataLayer';
import { DashboardAnnotationsDataLayer } from '../scene/DashboardAnnotationsDataLayer';
@ -66,7 +66,7 @@ describe('AnnotationsEditView', () => {
it('should return 0 if no annotations', () => {
dashboardScene.setState({
$data: new SceneDataLayers({ layers: [] }),
$data: new SceneDataLayerSet({ layers: [] }),
});
expect(annotationsView.getAnnotationsLength()).toBe(0);
@ -158,7 +158,7 @@ async function buildTestScene() {
meta: {
canEdit: true,
},
$data: new SceneDataLayers({
$data: new SceneDataLayerSet({
layers: [
new DashboardAnnotationsDataLayer({
key: `annotations-test`,

View File

@ -1,6 +1,6 @@
import React from 'react';
import { AnnotationQuery, DataTopic, NavModel, NavModelItem, PageLayoutType, getDataSourceRef } from '@grafana/data';
import { AnnotationQuery, NavModel, NavModelItem, PageLayoutType, getDataSourceRef } from '@grafana/data';
import { getDataSourceSrv } from '@grafana/runtime';
import { SceneComponentProps, SceneObjectBase, VizPanel, dataLayers } from '@grafana/scenes';
import { Page } from 'app/core/components/Page/Page';
@ -52,7 +52,7 @@ export class AnnotationsEditView extends SceneObjectBase<AnnotationsEditViewStat
public getAnnotationsLength(): number {
return dashboardSceneGraph
.getDataLayers(this._dashboard)
.state.layers.filter((layer) => layer.topic === DataTopic.Annotations).length;
.state.layers.filter((layer) => layer instanceof DashboardAnnotationsDataLayer).length;
}
public getDashboard(): DashboardScene {
@ -68,7 +68,6 @@ export class AnnotationsEditView extends SceneObjectBase<AnnotationsEditViewStat
};
const newAnnotation = new DashboardAnnotationsDataLayer({
key: `annotations-${newAnnotationQuery.name}`,
query: newAnnotationQuery,
name: newAnnotationQuery.name,
isEnabled: Boolean(newAnnotationQuery.enable),
@ -126,7 +125,6 @@ export class AnnotationsEditView extends SceneObjectBase<AnnotationsEditViewStat
const layer = this.getDataLayer(editIndex);
layer.setState({
key: `annotations-${annotation.name}`,
name: annotation.name,
isEnabled: Boolean(annotation.enable),
isHidden: Boolean(annotation.hide),

View File

@ -6,7 +6,7 @@ import {
SceneTimeRange,
VizPanel,
SceneDataTransformer,
SceneDataLayers,
SceneDataLayerSet,
} from '@grafana/scenes';
import { DashboardCursorSync } from '@grafana/schema';
import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard';
@ -151,7 +151,7 @@ function setup() {
weekStart: 'friday',
timeZone: 'America/New_York',
}),
$data: new SceneDataLayers({
$data: new SceneDataLayerSet({
layers: [
new DashboardAnnotationsDataLayer({
key: `annotations-test`,

View File

@ -4,7 +4,7 @@ import { AnnotationQuery, DashboardCursorSync, dateTimeFormat, DateTimeInput, Ev
import { TimeRangeUpdatedEvent } from '@grafana/runtime';
import {
behaviors,
SceneDataLayers,
SceneDataLayerSet,
sceneGraph,
SceneGridLayout,
SceneGridRow,
@ -113,7 +113,7 @@ export class DashboardModelCompatibilityWrapper {
public get annotations(): { list: AnnotationQuery[] } {
const annotations: { list: AnnotationQuery[] } = { list: [] };
if (this._scene.state.$data instanceof SceneDataLayers) {
if (this._scene.state.$data instanceof SceneDataLayerSet) {
annotations.list = dataLayersToAnnotations(this._scene.state.$data.state.layers);
}

View File

@ -1,5 +1,5 @@
import {
SceneDataLayers,
SceneDataLayerSet,
SceneGridLayout,
SceneGridRow,
SceneQueryRunner,
@ -75,7 +75,7 @@ describe('dashboardSceneGraph', () => {
it('should return the scene data layers', () => {
const dataLayers = dashboardSceneGraph.getDataLayers(scene);
expect(dataLayers).toBeInstanceOf(SceneDataLayers);
expect(dataLayers).toBeInstanceOf(SceneDataLayerSet);
expect(dataLayers?.state.layers.length).toBe(2);
});
@ -84,7 +84,7 @@ describe('dashboardSceneGraph', () => {
$data: undefined,
});
expect(() => dashboardSceneGraph.getDataLayers(scene)).toThrow('SceneDataLayers not found');
expect(() => dashboardSceneGraph.getDataLayers(scene)).toThrow('SceneDataLayerSet not found');
});
});
@ -241,7 +241,7 @@ function buildTestScene(overrides?: Partial<DashboardSceneState>) {
sync: DashboardCursorSync.Crosshair,
}),
],
$data: new SceneDataLayers({
$data: new SceneDataLayerSet({
layers: [
new DashboardAnnotationsDataLayer({
key: `annotation`,

View File

@ -1,4 +1,4 @@
import { VizPanel, SceneGridRow, SceneDataLayers, sceneGraph, SceneGridLayout, behaviors } from '@grafana/scenes';
import { VizPanel, SceneGridRow, SceneDataLayerSet, sceneGraph, SceneGridLayout, behaviors } from '@grafana/scenes';
import { DashboardGridItem } from '../scene/DashboardGridItem';
import { DashboardScene } from '../scene/DashboardScene';
@ -53,11 +53,11 @@ function getVizPanels(scene: DashboardScene): VizPanel[] {
return panels;
}
function getDataLayers(scene: DashboardScene): SceneDataLayers {
function getDataLayers(scene: DashboardScene): SceneDataLayerSet {
const data = sceneGraph.getData(scene);
if (!(data instanceof SceneDataLayers)) {
throw new Error('SceneDataLayers not found');
if (!(data instanceof SceneDataLayerSet)) {
throw new Error('SceneDataLayerSet not found');
}
return data;

View File

@ -4126,9 +4126,9 @@ __metadata:
languageName: unknown
linkType: soft
"@grafana/scenes@npm:3.13.3":
version: 3.13.3
resolution: "@grafana/scenes@npm:3.13.3"
"@grafana/scenes@npm:4.0.1":
version: 4.0.1
resolution: "@grafana/scenes@npm:4.0.1"
dependencies:
"@grafana/e2e-selectors": "npm:10.3.3"
react-grid-layout: "npm:1.3.4"
@ -4142,7 +4142,7 @@ __metadata:
"@grafana/ui": ^10.0.3
react: ^18.0.0
react-dom: ^18.0.0
checksum: 10/5b7f2e2714dcdbc3ad58352ec0cc7f513f7a240dc11a3e309733a662760a598e9333ec377f2899b6b668e018c2e885e8a044af7d42d1be4487d692cda3b9359a
checksum: 10/e12737e1a73155ef48bc507b20a2e47d1d7784730c625d799ea74b8719f0bfa2babc193ecdbcb85727ac81dbba0bfad27d4090696b34a7ed1a97caa9813540de
languageName: node
linkType: hard
@ -5240,7 +5240,7 @@ __metadata:
languageName: node
linkType: hard
"@msagl/core@npm:^1.1.16, @msagl/core@npm:^1.1.18":
"@msagl/core@npm:^1.1.16":
version: 1.1.18
resolution: "@msagl/core@npm:1.1.18"
dependencies:
@ -5254,25 +5254,39 @@ __metadata:
languageName: node
linkType: hard
"@msagl/drawing@npm:^1.1.18":
version: 1.1.18
resolution: "@msagl/drawing@npm:1.1.18"
"@msagl/core@npm:^1.1.17":
version: 1.1.17
resolution: "@msagl/core@npm:1.1.17"
dependencies:
"@msagl/core": "npm:^1.1.18"
checksum: 10/4cba6bd172dc39e09da9487c87b9caf511fb12eff433715ba3efeac95650315c5a5fb8cc6dfffd6f76de304a8cab0ecb839fc933268f0a0a004ee130594e4773
"@esfx/collections": "npm:^1.0.0"
"@esfx/collections-sortedmap": "npm:^1.0.0"
queue-typescript: "npm:^1.0.1"
reliable-random: "npm:^0.0.1"
stack-typescript: "npm:^1.0.4"
typescript-string-operations: "npm:^1.4.1"
checksum: 10/6a0c83911d0802cf4372a9d976017117d9ecbec8e8bda8850fb58dec76558c0453290a51ad1c55b451ae70deeb3a57b1623bcbf7d054b3bfdacb9ea2a8190d5a
languageName: node
linkType: hard
"@msagl/drawing@npm:^1.1.17":
version: 1.1.17
resolution: "@msagl/drawing@npm:1.1.17"
dependencies:
"@msagl/core": "npm:^1.1.17"
checksum: 10/7a3f38cf2cafeda292cbd7c3f78d55f39f11a5923c3675ecd5a51d362660461fca50b42e5c9aa58431ad34f650fff0443be99670947c6fcd6f07701057a08c52
languageName: node
linkType: hard
"@msagl/parser@npm:^1.1.16":
version: 1.1.18
resolution: "@msagl/parser@npm:1.1.18"
version: 1.1.17
resolution: "@msagl/parser@npm:1.1.17"
dependencies:
"@msagl/core": "npm:^1.1.18"
"@msagl/drawing": "npm:^1.1.18"
"@msagl/core": "npm:^1.1.17"
"@msagl/drawing": "npm:^1.1.17"
"@types/parse-color": "npm:^1.0.1"
dotparser: "npm:^1.1.1"
parse-color: "npm:^1.0.0"
checksum: 10/f2b2d01d08e82341251cc8907a19881b2b203480342a49d14432922d68edf03a26bf3b4959d6673f45347afce7655f7cb95ace2218e06617cde5f4ed5a669f80
checksum: 10/eae9c0a02410177b9c8f73edcad3c9e4610a31a972b3c58a870fe1db7c75b857304a87b82e8b7f9d6afedf91be73969856626ebb0f3fdae0d47c4e62da805ca1
languageName: node
linkType: hard
@ -18540,7 +18554,7 @@ __metadata:
"@grafana/prometheus": "workspace:*"
"@grafana/runtime": "workspace:*"
"@grafana/saga-icons": "workspace:*"
"@grafana/scenes": "npm:3.13.3"
"@grafana/scenes": "npm:4.0.1"
"@grafana/schema": "workspace:*"
"@grafana/sql": "workspace:*"
"@grafana/tsconfig": "npm:^1.3.0-rc1"