The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/public/app/features/dashboard/state/getPanelOptionsWithDefaults.ts

218 lines
6.0 KiB

import {
ConfigOverrideRule,
DynamicConfigValue,
FieldColorConfigSettings,
FieldColorModeId,
fieldColorModeRegistry,
FieldConfigOptionsRegistry,
FieldConfigProperty,
FieldConfigSource,
PanelPlugin,
ThresholdsConfig,
ThresholdsMode,
} from '@grafana/data';
import { mergeWith, isArray, isObject, unset, isEqual } from 'lodash';
export interface Props {
plugin: PanelPlugin;
currentFieldConfig: FieldConfigSource;
currentOptions: Record<string, any>;
isAfterPluginChange: boolean;
}
export interface OptionDefaults {
options: any;
fieldConfig: FieldConfigSource;
}
export function getPanelOptionsWithDefaults({
plugin,
currentOptions,
currentFieldConfig,
isAfterPluginChange,
}: Props): OptionDefaults {
const optionsWithDefaults = mergeWith(
{},
plugin.defaults,
currentOptions || {},
(objValue: any, srcValue: any): any => {
if (isArray(srcValue)) {
return srcValue;
}
}
);
const fieldConfigWithDefaults = applyFieldConfigDefaults(currentFieldConfig, plugin);
const fieldConfigWithOptimalColorMode = adaptFieldColorMode(plugin, fieldConfigWithDefaults, isAfterPluginChange);
return { options: optionsWithDefaults, fieldConfig: fieldConfigWithOptimalColorMode };
}
function applyFieldConfigDefaults(existingFieldConfig: FieldConfigSource, plugin: PanelPlugin): FieldConfigSource {
const pluginDefaults = plugin.fieldConfigDefaults;
const result: FieldConfigSource = {
defaults: mergeWith(
{},
pluginDefaults.defaults,
existingFieldConfig ? existingFieldConfig.defaults : {},
(objValue: any, srcValue: any): any => {
if (isArray(srcValue)) {
return srcValue;
}
}
),
overrides: existingFieldConfig?.overrides ?? [],
};
cleanProperties(result.defaults, '', plugin.fieldConfigRegistry);
// Thresholds base values are null in JSON but need to be converted to -Infinity
if (result.defaults.thresholds) {
fixThresholds(result.defaults.thresholds);
}
// Filter out overrides for properties that cannot be found in registry
result.overrides = filterFieldConfigOverrides(result.overrides, (prop) => {
return plugin.fieldConfigRegistry.getIfExists(prop.id) !== undefined;
});
for (const override of result.overrides) {
for (const property of override.properties) {
if (property.id === 'thresholds') {
fixThresholds(property.value as ThresholdsConfig);
}
}
}
return result;
}
export function filterFieldConfigOverrides(
overrides: ConfigOverrideRule[],
condition: (value: DynamicConfigValue) => boolean
): ConfigOverrideRule[] {
return overrides
.map((x) => {
const properties = x.properties.filter(condition);
return {
...x,
properties,
};
})
.filter((x) => x.properties.length > 0);
}
function cleanProperties(obj: any, parentPath: string, fieldConfigRegistry: FieldConfigOptionsRegistry) {
let found = false;
for (const propName of Object.keys(obj)) {
const value = obj[propName];
const fullPath = `${parentPath}${propName}`;
const existsInRegistry = !!fieldConfigRegistry.getIfExists(fullPath);
// need to check early here as some standard properties have nested properies
if (existsInRegistry) {
found = true;
continue;
}
if (isArray(value) || !isObject(value)) {
if (!existsInRegistry) {
unset(obj, propName);
}
} else {
const childPropFound = cleanProperties(value, `${fullPath}.`, fieldConfigRegistry);
// If no child props found unset the main object
if (!childPropFound) {
unset(obj, propName);
}
}
}
return found;
}
function adaptFieldColorMode(
plugin: PanelPlugin,
fieldConfig: FieldConfigSource,
isAfterPluginChange: boolean
): FieldConfigSource {
if (!isAfterPluginChange) {
return fieldConfig;
}
// adjust to prefered field color setting if needed
const color = plugin.fieldConfigRegistry.getIfExists(FieldConfigProperty.Color);
if (color && color.settings) {
const colorSettings = color.settings as FieldColorConfigSettings;
const mode = fieldColorModeRegistry.getIfExists(fieldConfig.defaults.color?.mode);
// When no support fo value colors, use classic palette
if (!colorSettings.byValueSupport) {
if (!mode || mode.isByValue) {
fieldConfig.defaults.color = { mode: FieldColorModeId.PaletteClassic };
return fieldConfig;
}
}
// When supporting value colors and prefering thresholds, use Thresholds mode.
// Otherwise keep current mode
if (colorSettings.byValueSupport && colorSettings.preferThresholdsMode) {
if (!mode || !mode.isByValue) {
fieldConfig.defaults.color = { mode: FieldColorModeId.Thresholds };
return fieldConfig;
}
}
}
return fieldConfig;
}
function fixThresholds(thresholds: ThresholdsConfig) {
if (!thresholds.mode) {
thresholds.mode = ThresholdsMode.Absolute;
}
if (!thresholds.steps) {
thresholds.steps = [];
} else if (thresholds.steps.length) {
// First value is always -Infinity
// JSON saves it as null
thresholds.steps[0].value = -Infinity;
}
}
export function restoreCustomOverrideRules(current: FieldConfigSource, old: FieldConfigSource): FieldConfigSource {
const result = {
defaults: {
...current.defaults,
custom: old.defaults.custom,
},
overrides: [...current.overrides],
};
for (const override of old.overrides) {
for (const prop of override.properties) {
if (isCustomFieldProp(prop)) {
const currentOverride = result.overrides.find((o) => isEqual(o.matcher, override.matcher));
if (currentOverride) {
currentOverride.properties.push(prop);
} else {
result.overrides.push(override);
}
}
}
}
return result;
}
export function isCustomFieldProp(prop: DynamicConfigValue): boolean {
return prop.id.startsWith('custom.');
}
export function isStandardFieldProp(prop: DynamicConfigValue): boolean {
return !isCustomFieldProp(prop);
}