grafana/public/app/features/dashboard-scene/settings/variables/utils.ts
Sergej-Vlasov e1edec02d0
DashboardScene: Validate variable name in scenes variable editor (#82415)
* add variable name validation

* adjust variable name validation logic

* move variable name validation logic to model

* add tests for onValidateVariableName

* extract variable name validation itest into separate describe
2024-02-21 17:16:57 +02:00

201 lines
6.2 KiB
TypeScript

import { chain } from 'lodash';
import { DataSourceInstanceSettings, SelectableValue } from '@grafana/data';
import { config, getDataSourceSrv } from '@grafana/runtime';
import {
ConstantVariable,
CustomVariable,
DataSourceVariable,
IntervalVariable,
TextBoxVariable,
QueryVariable,
GroupByVariable,
SceneVariable,
MultiValueVariable,
AdHocFiltersVariable,
SceneVariableState,
} from '@grafana/scenes';
import { VariableType } from '@grafana/schema';
import { getIntervalsQueryFromNewIntervalModel } from '../../utils/utils';
import { AdHocFiltersVariableEditor } from './editors/AdHocFiltersVariableEditor';
import { ConstantVariableEditor } from './editors/ConstantVariableEditor';
import { CustomVariableEditor } from './editors/CustomVariableEditor';
import { DataSourceVariableEditor } from './editors/DataSourceVariableEditor';
import { GroupByVariableEditor } from './editors/GroupByVariableEditor';
import { IntervalVariableEditor } from './editors/IntervalVariableEditor';
import { QueryVariableEditor } from './editors/QueryVariableEditor';
import { TextBoxVariableEditor } from './editors/TextBoxVariableEditor';
interface EditableVariableConfig {
name: string;
description: string;
editor: React.ComponentType<any>;
}
export type EditableVariableType = Exclude<VariableType, 'system'>;
export function isEditableVariableType(type: VariableType): type is EditableVariableType {
return type !== 'system';
}
export const EDITABLE_VARIABLES: Record<EditableVariableType, EditableVariableConfig> = {
custom: {
name: 'Custom',
description: 'Define variable values manually',
editor: CustomVariableEditor,
},
query: {
name: 'Query',
description: 'Variable values are fetched from a datasource query',
editor: QueryVariableEditor,
},
constant: {
name: 'Constant',
description: 'Define a hidden constant variable, useful for metric prefixes in dashboards you want to share',
editor: ConstantVariableEditor,
},
interval: {
name: 'Interval',
description: 'Define a timespan interval (ex 1m, 1h, 1d)',
editor: IntervalVariableEditor,
},
datasource: {
name: 'Data source',
description: 'Enables you to dynamically switch the data source for multiple panels',
editor: DataSourceVariableEditor,
},
adhoc: {
name: 'Ad hoc filters',
description: 'Add key/value filters on the fly',
editor: AdHocFiltersVariableEditor,
},
groupby: {
name: 'Group by',
description: 'Add keys to group by on the fly',
editor: GroupByVariableEditor,
},
textbox: {
name: 'Textbox',
description: 'Define a textbox variable, where users can enter any arbitrary string',
editor: TextBoxVariableEditor,
},
};
export const EDITABLE_VARIABLES_SELECT_ORDER: EditableVariableType[] = [
'query',
'custom',
'textbox',
'constant',
'datasource',
'interval',
'adhoc',
'groupby',
];
export function getVariableTypeSelectOptions(): Array<SelectableValue<EditableVariableType>> {
const results = EDITABLE_VARIABLES_SELECT_ORDER.map((variableType) => ({
label: EDITABLE_VARIABLES[variableType].name,
value: variableType,
description: EDITABLE_VARIABLES[variableType].description,
}));
if (!config.featureToggles.groupByVariable) {
// Remove group by variable type if feature toggle is off
return results.filter((option) => option.value !== 'groupby');
}
return results;
}
export function getVariableEditor(type: EditableVariableType) {
return EDITABLE_VARIABLES[type].editor;
}
interface CommonVariableProperties {
name: string;
label?: string;
}
export function getVariableScene(type: EditableVariableType, initialState: CommonVariableProperties) {
switch (type) {
case 'custom':
return new CustomVariable(initialState);
case 'query':
return new QueryVariable(initialState);
case 'constant':
return new ConstantVariable(initialState);
case 'interval':
return new IntervalVariable(initialState);
case 'datasource':
return new DataSourceVariable(initialState);
case 'adhoc':
return new AdHocFiltersVariable(initialState);
case 'groupby':
return new GroupByVariable(initialState);
case 'textbox':
return new TextBoxVariable(initialState);
}
}
export function getVariableDefault(variables: Array<SceneVariable<SceneVariableState>>) {
const defaultVariableType = 'query';
const nextVariableIdName = getNextAvailableId(defaultVariableType, variables);
return new QueryVariable({
name: nextVariableIdName,
});
}
export function getNextAvailableId(type: VariableType, variables: Array<SceneVariable<SceneVariableState>>): string {
let counter = 0;
let nextId = `${type}${counter}`;
while (variables.find((variable) => variable.state.name === nextId)) {
nextId = `${type}${++counter}`;
}
return nextId;
}
export function hasVariableOptions(variable: SceneVariable): variable is MultiValueVariable {
// variable options can be defined by state.options or state.intervals in case of interval variable
return 'options' in variable.state || 'intervals' in variable.state;
}
export function getDefinition(model: SceneVariable): string {
let definition = '';
if (model instanceof QueryVariable) {
definition = model.state.definition || (typeof model.state.query === 'string' ? model.state.query : '');
} else if (model instanceof DataSourceVariable) {
definition = String(model.state.pluginId);
} else if (model instanceof CustomVariable) {
definition = model.state.query;
} else if (model instanceof IntervalVariable) {
definition = getIntervalsQueryFromNewIntervalModel(model.state.intervals);
} else if (model instanceof TextBoxVariable || model instanceof ConstantVariable) {
definition = String(model.state.value);
}
return definition;
}
export function getOptionDataSourceTypes() {
const datasources = getDataSourceSrv().getList({ metrics: true, variables: true });
const optionTypes = chain(datasources)
.uniqBy('meta.id')
.map((ds: DataSourceInstanceSettings) => {
return { label: ds.meta.name, value: ds.meta.id };
})
.value();
optionTypes.unshift({ label: '', value: '' });
return optionTypes;
}
export const RESERVED_GLOBAL_VARIABLE_NAME_REGEX = /^(?!__).*$/;
export const WORD_CHARACTERS_REGEX = /^\w+$/;