Dashboards: Changes are not detected from worker (#102105)

This commit is contained in:
Ivan Ortega Alba
2025-03-13 15:02:03 +01:00
committed by GitHub
parent 5d94293305
commit 772432e025
4 changed files with 35 additions and 28 deletions
@@ -137,9 +137,7 @@ export class DashboardSceneChangeTracker {
return this._dashboard.state.meta.folderUid !== this._dashboard.getInitialState()?.meta.folderUid;
}
private updateIsDirty(result: DashboardChangeInfo) {
const { hasChanges } = result;
private updateIsDirty(hasChanges: boolean) {
if (hasChanges || this.hasMetadataChanges()) {
if (!this._dashboard.state.isDirty) {
this._dashboard.setState({ isDirty: true });
@@ -161,7 +159,7 @@ export class DashboardSceneChangeTracker {
}
this._changesWorker!.onmessage = (e: MessageEvent<DashboardChangeInfo>) => {
this.updateIsDirty(e.data);
this.updateIsDirty(!!e.data.hasChanges);
};
const performSaveModelDiff = getChangeTrackerDebouncer(this.detectSaveModelChanges.bind(this));
@@ -1,6 +1,7 @@
import { Dashboard } from '@grafana/schema';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { getRawDashboardChanges } from './getDashboardChanges';
import { jsonDiff } from '../settings/version-history/utils';
function _debounce<T>(f: (...args: T[]) => void, timeout: number) {
let timeoutId: NodeJS.Timeout | undefined = undefined;
@@ -13,6 +14,29 @@ function _debounce<T>(f: (...args: T[]) => void, timeout: number) {
}
self.onmessage = _debounce((e: MessageEvent<{ initial: Dashboard; changed: Dashboard }>) => {
const result = getRawDashboardChanges(e.data.initial, e.data.changed, false, false, false);
const result = detectDashboardChanges(e.data.initial, e.data.changed);
self.postMessage(result);
}, 500);
export function detectDashboardChanges(
changedSaveModel: DashboardV2Spec | Dashboard,
initialSaveModel: DashboardV2Spec | Dashboard
) {
// Calculate differences using the non-transformed to v2 spec values to be able to compare the initial and changed dashboard values
const diff = jsonDiff(initialSaveModel, changedSaveModel);
const diffCount = Object.values(diff).reduce((acc, cur) => acc + cur.length, 0);
const hasMigratedToV2 = isDashboardV2Spec(changedSaveModel) && !isDashboardV2Spec(initialSaveModel);
return {
changedSaveModel,
initialSaveModel,
diffs: diff,
diffCount,
hasChanges: diffCount > 0,
hasMigratedToV2,
};
}
export function isDashboardV2Spec(obj: Dashboard | DashboardV2Spec): obj is DashboardV2Spec {
return 'elements' in obj;
}
@@ -8,8 +8,6 @@ jest.mocked(worker.postMessage).mockImplementation(() => {
worker.onmessage?.({
data: {
hasChanges: true,
hasTimeChanges: true,
hasVariableValueChanges: true,
},
} as unknown as MessageEvent);
});
@@ -81,7 +81,7 @@ locationUtil.initialize({
});
const worker = createWorker();
mockResultsOfDetectChangesWorker({ hasChanges: true, hasTimeChanges: false, hasVariableValueChanges: false });
mockResultsOfDetectChangesWorker({ hasChanges: true });
describe('DashboardScene', () => {
describe('DashboardSrv.getCurrent compatibility', () => {
@@ -224,7 +224,7 @@ describe('DashboardScene', () => {
const prevMeta = { ...scene.state.meta };
// The worker only detects changes in the model, so the folder change should be detected anyway
mockResultsOfDetectChangesWorker({ hasChanges: false, hasTimeChanges: false, hasVariableValueChanges: false });
mockResultsOfDetectChangesWorker({ hasChanges: false });
scene.setState({
meta: {
@@ -706,7 +706,7 @@ describe('DashboardScene', () => {
});
it('A change to a variable state should set isDirty true', () => {
mockResultsOfDetectChangesWorker({ hasChanges: true, hasTimeChanges: false, hasVariableValueChanges: true });
mockResultsOfDetectChangesWorker({ hasChanges: true });
const variable = new TestVariable({ name: 'A' });
const scene = buildTestScene({
$variables: new SceneVariableSet({ variables: [variable] }),
@@ -730,13 +730,10 @@ describe('DashboardScene', () => {
scene.activate();
scene.onEnterEditMode();
mockResultsOfDetectChangesWorker({ hasChanges: true, hasTimeChanges: false, hasVariableValueChanges: false });
mockResultsOfDetectChangesWorker({ hasChanges: true });
variable.setState({ name: 'B' });
expect(scene.state.isDirty).toBe(true);
mockResultsOfDetectChangesWorker(
// No changes, it is the same name than before comparing saving models
{ hasChanges: false, hasTimeChanges: false, hasVariableValueChanges: false }
);
mockResultsOfDetectChangesWorker({ hasChanges: false });
variable.setState({ name: 'A' });
expect(scene.state.isDirty).toBe(false);
});
@@ -959,21 +956,11 @@ function buildTestScene(overrides?: Partial<DashboardSceneState>) {
return scene;
}
function mockResultsOfDetectChangesWorker({
hasChanges,
hasTimeChanges,
hasVariableValueChanges,
}: {
hasChanges: boolean;
hasTimeChanges: boolean;
hasVariableValueChanges: boolean;
}) {
function mockResultsOfDetectChangesWorker({ hasChanges = true }) {
jest.mocked(worker.postMessage).mockImplementationOnce(() => {
worker.onmessage?.({
data: {
hasChanges: hasChanges ?? true,
hasTimeChanges: hasTimeChanges ?? true,
hasVariableValueChanges: hasVariableValueChanges ?? true,
hasChanges,
},
} as unknown as MessageEvent);
});