2024-03-01 00:18:26 +08:00
|
|
|
// @ts-ignore
|
|
|
|
|
2024-04-25 21:42:24 +08:00
|
|
|
import type { AdHocVariableModel, TypedVariableModel } from '@grafana/data';
|
2024-05-09 23:48:20 +08:00
|
|
|
import { Dashboard, Panel, VariableOption } from '@grafana/schema';
|
2024-12-19 19:00:59 +08:00
|
|
|
import {
|
|
|
|
AdHocFilterWithLabels,
|
|
|
|
AdhocVariableSpec,
|
|
|
|
DashboardV2Spec,
|
|
|
|
VariableKind,
|
2025-01-16 20:18:47 +08:00
|
|
|
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
|
2024-05-02 22:41:13 +08:00
|
|
|
|
|
|
|
import { jsonDiff } from '../settings/version-history/utils';
|
2024-04-25 21:42:24 +08:00
|
|
|
|
|
|
|
export function get(obj: any, keys: string[]) {
|
|
|
|
try {
|
|
|
|
let val = obj;
|
|
|
|
for (const key of keys) {
|
|
|
|
val = val[key];
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
} catch (err) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function deepEqual(a: string | string[], b: string | string[]) {
|
|
|
|
return (
|
|
|
|
typeof a === typeof b &&
|
|
|
|
((typeof a === 'string' && a === b) ||
|
|
|
|
(Array.isArray(a) && a.length === b.length && a.every((val, i) => val === b[i])))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function isEqual(a: VariableOption | undefined, b: VariableOption | undefined) {
|
|
|
|
return a === b || (a && b && a.selected === b.selected && deepEqual(a.text, b.text) && deepEqual(a.value, b.value));
|
|
|
|
}
|
2024-03-01 00:18:26 +08:00
|
|
|
|
2024-12-13 17:20:22 +08:00
|
|
|
export function getRawDashboardV2Changes(
|
|
|
|
initial: DashboardV2Spec,
|
|
|
|
changed: DashboardV2Spec,
|
|
|
|
saveTimeRange?: boolean,
|
|
|
|
saveVariables?: boolean,
|
|
|
|
saveRefresh?: boolean
|
|
|
|
) {
|
2024-12-19 19:00:59 +08:00
|
|
|
const initialSaveModel = initial;
|
|
|
|
const changedSaveModel = changed;
|
|
|
|
const hasTimeChanged = getHasTimeChanged(changedSaveModel.timeSettings, initialSaveModel.timeSettings);
|
|
|
|
const hasVariableValueChanges = applyVariableChangesV2(changedSaveModel, initialSaveModel, saveVariables);
|
|
|
|
const hasRefreshChanged = changedSaveModel.timeSettings.autoRefresh !== initialSaveModel.timeSettings.autoRefresh;
|
|
|
|
|
|
|
|
if (!saveTimeRange) {
|
|
|
|
changedSaveModel.timeSettings.from = initialSaveModel.timeSettings.from;
|
|
|
|
changedSaveModel.timeSettings.to = initialSaveModel.timeSettings.to;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!saveRefresh) {
|
|
|
|
changedSaveModel.timeSettings.autoRefresh = initialSaveModel.timeSettings.autoRefresh;
|
|
|
|
}
|
|
|
|
|
|
|
|
const diff = jsonDiff(initialSaveModel, changedSaveModel);
|
|
|
|
const diffCount = Object.values(diff).reduce((acc, cur) => acc + cur.length, 0);
|
|
|
|
|
2024-12-13 17:20:22 +08:00
|
|
|
return {
|
2024-12-19 19:00:59 +08:00
|
|
|
changedSaveModel,
|
|
|
|
initialSaveModel,
|
|
|
|
diffs: diff,
|
|
|
|
diffCount,
|
|
|
|
hasChanges: diffCount > 0,
|
|
|
|
hasTimeChanges: hasTimeChanged,
|
|
|
|
hasVariableValueChanges,
|
|
|
|
hasRefreshChange: hasRefreshChanged,
|
2024-12-13 17:20:22 +08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getRawDashboardChanges(
|
2024-03-01 00:18:26 +08:00
|
|
|
initial: Dashboard,
|
|
|
|
changed: Dashboard,
|
|
|
|
saveTimeRange?: boolean,
|
2024-04-05 23:31:40 +08:00
|
|
|
saveVariables?: boolean,
|
|
|
|
saveRefresh?: boolean
|
2024-03-01 00:18:26 +08:00
|
|
|
) {
|
|
|
|
const initialSaveModel = initial;
|
|
|
|
const changedSaveModel = changed;
|
2024-12-19 19:00:59 +08:00
|
|
|
const hasTimeChanged = getHasTimeChanged(changedSaveModel.time, initialSaveModel.time);
|
2024-03-01 00:18:26 +08:00
|
|
|
const hasVariableValueChanges = applyVariableChanges(changedSaveModel, initialSaveModel, saveVariables);
|
2024-04-05 23:31:40 +08:00
|
|
|
const hasRefreshChanged = changedSaveModel.refresh !== initialSaveModel.refresh;
|
2024-03-01 00:18:26 +08:00
|
|
|
|
|
|
|
if (!saveTimeRange) {
|
|
|
|
changedSaveModel.time = initialSaveModel.time;
|
|
|
|
}
|
|
|
|
|
2024-04-05 23:31:40 +08:00
|
|
|
if (!saveRefresh) {
|
|
|
|
changedSaveModel.refresh = initialSaveModel.refresh;
|
|
|
|
}
|
|
|
|
|
2024-03-01 00:18:26 +08:00
|
|
|
const diff = jsonDiff(initialSaveModel, changedSaveModel);
|
2024-04-25 21:42:24 +08:00
|
|
|
const diffCount = Object.values(diff).reduce((acc, cur) => acc + cur.length, 0);
|
2024-03-01 00:18:26 +08:00
|
|
|
|
|
|
|
return {
|
|
|
|
changedSaveModel,
|
|
|
|
initialSaveModel,
|
|
|
|
diffs: diff,
|
|
|
|
diffCount,
|
|
|
|
hasChanges: diffCount > 0,
|
|
|
|
hasTimeChanges: hasTimeChanged,
|
|
|
|
isNew: changedSaveModel.version === 0,
|
|
|
|
hasVariableValueChanges,
|
2024-04-05 23:31:40 +08:00
|
|
|
hasRefreshChange: hasRefreshChanged,
|
2024-03-01 00:18:26 +08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-12-19 19:00:59 +08:00
|
|
|
interface DefaultPersistedTimeValue {
|
|
|
|
from?: string;
|
|
|
|
to?: string;
|
|
|
|
}
|
|
|
|
export function getHasTimeChanged(
|
|
|
|
newRange: DefaultPersistedTimeValue = {},
|
|
|
|
previousRange: DefaultPersistedTimeValue = {}
|
|
|
|
) {
|
|
|
|
return newRange.from !== previousRange.from || newRange.to !== previousRange.to;
|
2024-03-01 00:18:26 +08:00
|
|
|
}
|
|
|
|
|
2024-12-19 19:00:59 +08:00
|
|
|
export function adHocVariableFiltersEqual(filtersA?: AdHocFilterWithLabels[], filtersB?: AdHocFilterWithLabels[]) {
|
|
|
|
if (filtersA === undefined && filtersB === undefined) {
|
2024-06-11 17:06:19 +08:00
|
|
|
console.warn('Adhoc variable filter property is undefined');
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-12-19 19:00:59 +08:00
|
|
|
if ((filtersA === undefined && filtersB !== undefined) || (filtersB === undefined && filtersA !== undefined)) {
|
2024-06-11 17:06:19 +08:00
|
|
|
console.warn('Adhoc variable filter property is undefined');
|
|
|
|
return false;
|
2024-05-24 19:33:05 +08:00
|
|
|
}
|
|
|
|
|
2024-12-19 19:00:59 +08:00
|
|
|
if (filtersA?.length !== filtersB?.length) {
|
2024-05-24 19:33:05 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-12-19 19:00:59 +08:00
|
|
|
for (let i = 0; i < (filtersA?.length ?? 0); i++) {
|
|
|
|
const aFilter = filtersA?.[i];
|
|
|
|
const bFilter = filtersB?.[i];
|
|
|
|
if (aFilter?.key !== bFilter?.key || aFilter?.operator !== bFilter?.operator || aFilter?.value !== bFilter?.value) {
|
2024-05-24 19:33:05 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-12-19 19:00:59 +08:00
|
|
|
export function applyVariableChangesV2(
|
|
|
|
saveModel: DashboardV2Spec,
|
|
|
|
originalSaveModel: DashboardV2Spec,
|
|
|
|
saveVariables?: boolean
|
|
|
|
) {
|
|
|
|
const originalVariables = originalSaveModel.variables ?? [];
|
|
|
|
const variablesToSave = saveModel.variables ?? [];
|
|
|
|
let hasVariableValueChanges = false;
|
|
|
|
|
|
|
|
for (const variable of variablesToSave) {
|
|
|
|
const hasCurrentValueToSave = (v: VariableKind) =>
|
|
|
|
v.kind === 'QueryVariable' ||
|
|
|
|
v.kind === 'CustomVariable' ||
|
|
|
|
v.kind === 'DatasourceVariable' ||
|
|
|
|
v.kind === 'ConstantVariable' ||
|
|
|
|
v.kind === 'IntervalVariable' ||
|
|
|
|
v.kind === 'TextVariable' ||
|
|
|
|
v.kind === 'GroupByVariable';
|
|
|
|
|
|
|
|
const hasOptionsToSave = (v: VariableKind) =>
|
|
|
|
v.kind === 'QueryVariable' ||
|
|
|
|
v.kind === 'CustomVariable' ||
|
|
|
|
v.kind === 'DatasourceVariable' ||
|
|
|
|
v.kind === 'IntervalVariable' ||
|
|
|
|
v.kind === 'GroupByVariable';
|
|
|
|
|
|
|
|
const original = originalVariables.find(
|
|
|
|
({ spec, kind }) => spec.name === variable.spec.name && kind === variable.kind
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!original) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
hasCurrentValueToSave(variable) &&
|
|
|
|
hasCurrentValueToSave(original) &&
|
|
|
|
!isEqual(variable.spec.current, original.spec.current)
|
|
|
|
) {
|
|
|
|
hasVariableValueChanges = true;
|
|
|
|
} else if (
|
|
|
|
variable.kind === 'AdhocVariable' &&
|
|
|
|
original.kind === 'AdhocVariable' &&
|
|
|
|
!adHocVariableFiltersEqual(variable.spec.filters, original.spec.filters)
|
|
|
|
) {
|
|
|
|
hasVariableValueChanges = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!saveVariables) {
|
|
|
|
if (variable.kind === 'AdhocVariable') {
|
|
|
|
variable.spec.filters = (original.spec as AdhocVariableSpec).filters;
|
|
|
|
} else {
|
|
|
|
if (hasCurrentValueToSave(variable) && hasCurrentValueToSave(original)) {
|
|
|
|
variable.spec.current = original.spec.current;
|
|
|
|
}
|
|
|
|
if (hasOptionsToSave(variable) && hasOptionsToSave(original)) {
|
|
|
|
variable.spec.options = original.spec.options;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return hasVariableValueChanges;
|
|
|
|
}
|
|
|
|
|
2024-03-01 00:18:26 +08:00
|
|
|
export function applyVariableChanges(saveModel: Dashboard, originalSaveModel: Dashboard, saveVariables?: boolean) {
|
|
|
|
const originalVariables = originalSaveModel.templating?.list ?? [];
|
|
|
|
const variablesToSave = saveModel.templating?.list ?? [];
|
|
|
|
let hasVariableValueChanges = false;
|
|
|
|
|
|
|
|
for (const variable of variablesToSave) {
|
|
|
|
const original = originalVariables.find(({ name, type }) => name === variable.name && type === variable.type);
|
|
|
|
|
|
|
|
if (!original) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Old schema property that never should be in persisted model
|
2024-04-25 21:42:24 +08:00
|
|
|
if (original.current) {
|
2024-03-01 00:18:26 +08:00
|
|
|
delete original.current.selected;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isEqual(variable.current, original.current)) {
|
|
|
|
hasVariableValueChanges = true;
|
2024-05-24 19:33:05 +08:00
|
|
|
} else if (
|
|
|
|
variable.type === 'adhoc' &&
|
2024-12-19 19:00:59 +08:00
|
|
|
!adHocVariableFiltersEqual(
|
|
|
|
(variable as AdHocVariableModel | undefined)?.filters,
|
|
|
|
(original as AdHocVariableModel | undefined)?.filters
|
|
|
|
)
|
2024-05-24 19:33:05 +08:00
|
|
|
) {
|
|
|
|
hasVariableValueChanges = true;
|
2024-03-01 00:18:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!saveVariables) {
|
|
|
|
const typed = variable as TypedVariableModel;
|
|
|
|
if (typed.type === 'adhoc') {
|
|
|
|
typed.filters = (original as AdHocVariableModel).filters;
|
2024-05-24 19:33:05 +08:00
|
|
|
} else {
|
2024-04-25 21:42:24 +08:00
|
|
|
variable.current = original.current;
|
|
|
|
variable.options = original.options;
|
2024-03-01 00:18:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return hasVariableValueChanges;
|
|
|
|
}
|
2024-05-09 23:48:20 +08:00
|
|
|
|
|
|
|
export function getPanelChanges(saveModel: Panel, originalSaveModel: Panel) {
|
|
|
|
const diff = jsonDiff(originalSaveModel, saveModel);
|
|
|
|
const diffCount = Object.values(diff).reduce((acc, cur) => acc + cur.length, 0);
|
|
|
|
|
|
|
|
return {
|
|
|
|
changedSaveModel: saveModel,
|
|
|
|
initialSaveModel: originalSaveModel,
|
|
|
|
diffs: diff,
|
|
|
|
diffCount,
|
|
|
|
hasChanges: diffCount > 0,
|
|
|
|
};
|
|
|
|
}
|