Chore: Change VariableModel to TypedVariableModel in most places (#76690)

This commit is contained in:
kay delaney 2023-11-08 22:15:29 +00:00 committed by GitHub
parent 3559cb4e81
commit 375dcc3813
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 134 additions and 147 deletions

View File

@ -1,4 +1,12 @@
import { LoadingState } from '@grafana/data'; import {
LoadingState,
ConstantVariableModel,
CustomVariableModel,
DataSourceVariableModel,
QueryVariableModel,
IntervalVariableModel,
TypedVariableModel,
} from '@grafana/data';
import { getPanelPlugin } from '@grafana/data/test/__mocks__/pluginMocks'; import { getPanelPlugin } from '@grafana/data/test/__mocks__/pluginMocks';
import { config } from '@grafana/runtime'; import { config } from '@grafana/runtime';
import { import {
@ -374,7 +382,7 @@ describe('transformSaveModelToScene', () => {
describe('when creating variables objects', () => { describe('when creating variables objects', () => {
it('should migrate custom variable', () => { it('should migrate custom variable', () => {
const variable = { const variable: CustomVariableModel = {
current: { current: {
selected: false, selected: false,
text: 'a', text: 'a',
@ -408,12 +416,12 @@ describe('transformSaveModelToScene', () => {
], ],
query: 'a,b,c,d', query: 'a,b,c,d',
skipUrlSync: false, skipUrlSync: false,
type: 'custom' as VariableType, type: 'custom',
rootStateKey: 'N4XLmH5Vz', rootStateKey: 'N4XLmH5Vz',
id: 'query0', id: 'query0',
global: false, global: false,
index: 0, index: 0,
state: 'Done', state: LoadingState.Done,
error: null, error: null,
description: null, description: null,
allValue: null, allValue: null,
@ -442,7 +450,7 @@ describe('transformSaveModelToScene', () => {
}); });
it('should migrate query variable', () => { it('should migrate query variable', () => {
const variable = { const variable: QueryVariableModel = {
allValue: null, allValue: null,
current: { current: {
text: 'America', text: 'America',
@ -486,15 +494,12 @@ describe('transformSaveModelToScene', () => {
regex: '', regex: '',
skipUrlSync: false, skipUrlSync: false,
sort: 0, sort: 0,
tagValuesQuery: null, type: 'query',
tagsQuery: null,
type: 'query' as VariableType,
useTags: false,
rootStateKey: '000000002', rootStateKey: '000000002',
id: 'datacenter', id: 'datacenter',
global: false, global: false,
index: 0, index: 0,
state: 'Done', state: LoadingState.Done,
error: null, error: null,
description: null, description: null,
}; };
@ -529,16 +534,16 @@ describe('transformSaveModelToScene', () => {
}); });
it('should migrate datasource variable', () => { it('should migrate datasource variable', () => {
const variable = { const variable: DataSourceVariableModel = {
id: 'query1', id: 'query1',
rootStateKey: 'N4XLmH5Vz', rootStateKey: 'N4XLmH5Vz',
name: 'query1', name: 'query1',
type: 'datasource' as VariableType, type: 'datasource',
global: false, global: false,
index: 1, index: 1,
hide: 0, hide: 0,
skipUrlSync: false, skipUrlSync: false,
state: 'Done', state: LoadingState.Done,
error: null, error: null,
description: null, description: null,
current: { current: {
@ -595,12 +600,12 @@ describe('transformSaveModelToScene', () => {
}); });
it('should migrate constant variable', () => { it('should migrate constant variable', () => {
const variable = { const variable: ConstantVariableModel = {
hide: 2, hide: 2,
label: 'constant', label: 'constant',
name: 'constant', name: 'constant',
skipUrlSync: false, skipUrlSync: false,
type: 'constant' as VariableType, type: 'constant',
rootStateKey: 'N4XLmH5Vz', rootStateKey: 'N4XLmH5Vz',
current: { current: {
selected: true, selected: true,
@ -618,7 +623,7 @@ describe('transformSaveModelToScene', () => {
id: 'constant', id: 'constant',
global: false, global: false,
index: 3, index: 3,
state: 'Done', state: LoadingState.Done,
error: null, error: null,
description: null, description: null,
}; };
@ -638,10 +643,10 @@ describe('transformSaveModelToScene', () => {
}); });
it('should migrate interval variable', () => { it('should migrate interval variable', () => {
const variable = { const variable: IntervalVariableModel = {
name: 'intervalVar', name: 'intervalVar',
label: 'Interval Label', label: 'Interval Label',
type: 'interval' as VariableType, type: 'interval',
rootStateKey: 'N4XLmH5Vz', rootStateKey: 'N4XLmH5Vz',
auto: false, auto: false,
refresh: 2, refresh: 2,
@ -665,7 +670,7 @@ describe('transformSaveModelToScene', () => {
index: 4, index: 4,
hide: 0, hide: 0,
skipUrlSync: false, skipUrlSync: false,
state: 'Done', state: LoadingState.Done,
error: null, error: null,
description: null, description: null,
}; };
@ -692,7 +697,7 @@ describe('transformSaveModelToScene', () => {
type: type as VariableType, type: type as VariableType,
}; };
expect(() => createSceneVariableFromVariableModel(variable)).toThrow(); expect(() => createSceneVariableFromVariableModel(variable as TypedVariableModel)).toThrow();
}); });
}); });

View File

@ -1,12 +1,4 @@
import { import { AdHocVariableModel, TypedVariableModel, VariableModel } from '@grafana/data';
AdHocVariableModel,
ConstantVariableModel,
CustomVariableModel,
DataSourceVariableModel,
IntervalVariableModel,
QueryVariableModel,
VariableModel,
} from '@grafana/data';
import { import {
VizPanel, VizPanel,
SceneTimePicker, SceneTimePicker,
@ -252,12 +244,12 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel)
}); });
} }
export function createSceneVariableFromVariableModel(variable: VariableModel): SceneVariable { export function createSceneVariableFromVariableModel(variable: TypedVariableModel): SceneVariable {
const commonProperties = { const commonProperties = {
name: variable.name, name: variable.name,
label: variable.label, label: variable.label,
}; };
if (isCustomVariable(variable)) { if (variable.type === 'custom') {
return new CustomVariable({ return new CustomVariable({
...commonProperties, ...commonProperties,
value: variable.current.value, value: variable.current.value,
@ -271,7 +263,7 @@ export function createSceneVariableFromVariableModel(variable: VariableModel): S
skipUrlSync: variable.skipUrlSync, skipUrlSync: variable.skipUrlSync,
hide: variable.hide, hide: variable.hide,
}); });
} else if (isQueryVariable(variable)) { } else if (variable.type === 'query') {
return new QueryVariable({ return new QueryVariable({
...commonProperties, ...commonProperties,
value: variable.current.value, value: variable.current.value,
@ -289,7 +281,7 @@ export function createSceneVariableFromVariableModel(variable: VariableModel): S
skipUrlSync: variable.skipUrlSync, skipUrlSync: variable.skipUrlSync,
hide: variable.hide, hide: variable.hide,
}); });
} else if (isDataSourceVariable(variable)) { } else if (variable.type === 'datasource') {
return new DataSourceVariable({ return new DataSourceVariable({
...commonProperties, ...commonProperties,
value: variable.current.value, value: variable.current.value,
@ -304,7 +296,7 @@ export function createSceneVariableFromVariableModel(variable: VariableModel): S
isMulti: variable.multi, isMulti: variable.multi,
hide: variable.hide, hide: variable.hide,
}); });
} else if (isIntervalVariable(variable)) { } else if (variable.type === 'interval') {
const intervals = getIntervalsFromOldIntervalModel(variable); const intervals = getIntervalsFromOldIntervalModel(variable);
const currentInterval = getCurrentValueForOldIntervalModel(variable, intervals); const currentInterval = getCurrentValueForOldIntervalModel(variable, intervals);
return new IntervalVariable({ return new IntervalVariable({
@ -319,7 +311,7 @@ export function createSceneVariableFromVariableModel(variable: VariableModel): S
skipUrlSync: variable.skipUrlSync, skipUrlSync: variable.skipUrlSync,
hide: variable.hide, hide: variable.hide,
}); });
} else if (isConstantVariable(variable)) { } else if (variable.type === 'constant') {
return new ConstantVariable({ return new ConstantVariable({
...commonProperties, ...commonProperties,
description: variable.description, description: variable.description,
@ -417,9 +409,4 @@ export function buildGridItemForPanel(panel: PanelModel): SceneGridItemLike {
}); });
} }
const isCustomVariable = (v: VariableModel): v is CustomVariableModel => v.type === 'custom';
const isQueryVariable = (v: VariableModel): v is QueryVariableModel => v.type === 'query';
const isDataSourceVariable = (v: VariableModel): v is DataSourceVariableModel => v.type === 'datasource';
const isConstantVariable = (v: VariableModel): v is ConstantVariableModel => v.type === 'constant';
const isIntervalVariable = (v: VariableModel): v is IntervalVariableModel => v.type === 'interval';
const isAdhocVariable = (v: VariableModel): v is AdHocVariableModel => v.type === 'adhoc'; const isAdhocVariable = (v: VariableModel): v is AdHocVariableModel => v.type === 'adhoc';

View File

@ -1,9 +1,9 @@
import { TypedVariableModel } from '@grafana/data';
import { DataSourceRef, DataQuery } from '@grafana/data/src/types/query'; import { DataSourceRef, DataQuery } from '@grafana/data/src/types/query';
import { DataSourceWithBackend } from '@grafana/runtime'; import { DataSourceWithBackend } from '@grafana/runtime';
import { updateConfig } from 'app/core/config'; import { updateConfig } from 'app/core/config';
import { mockDataSource } from 'app/features/alerting/unified/mocks'; import { mockDataSource } from 'app/features/alerting/unified/mocks';
import { PanelModel } from 'app/features/dashboard/state/PanelModel'; import { PanelModel } from 'app/features/dashboard/state/PanelModel';
import { VariableModel } from 'app/features/variables/types';
import { import {
PublicDashboard, PublicDashboard,
@ -38,13 +38,13 @@ jest.mock('@grafana/runtime/src/services/dataSourceSrv', () => {
describe('dashboardHasTemplateVariables', () => { describe('dashboardHasTemplateVariables', () => {
it('false', () => { it('false', () => {
let variables: VariableModel[] = []; let variables: TypedVariableModel[] = [];
expect(dashboardHasTemplateVariables(variables)).toBe(false); expect(dashboardHasTemplateVariables(variables)).toBe(false);
}); });
it('true', () => { it('true', () => {
//@ts-ignore //@ts-ignore
let variables: VariableModel[] = ['a']; let variables: TypedVariableModel[] = ['a'];
expect(dashboardHasTemplateVariables(variables)).toBe(true); expect(dashboardHasTemplateVariables(variables)).toBe(true);
}); });
}); });

View File

@ -1,7 +1,7 @@
import { TypedVariableModel } from '@grafana/data';
import { DataSourceWithBackend } from '@grafana/runtime'; import { DataSourceWithBackend } from '@grafana/runtime';
import { getConfig } from 'app/core/config'; import { getConfig } from 'app/core/config';
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { VariableModel } from 'app/features/variables/types';
import { PanelModel } from '../../../state'; import { PanelModel } from '../../../state';
import { shareDashboardType } from '../utils'; import { shareDashboardType } from '../utils';
@ -42,7 +42,7 @@ export interface SessionUser {
} }
// Instance methods // Instance methods
export const dashboardHasTemplateVariables = (variables: VariableModel[]): boolean => { export const dashboardHasTemplateVariables = (variables: TypedVariableModel[]): boolean => {
return variables.length > 0; return variables.length > 0;
}; };

View File

@ -2,12 +2,11 @@ import { css } from '@emotion/css';
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { connect, MapStateToProps } from 'react-redux'; import { connect, MapStateToProps } from 'react-redux';
import { AnnotationQuery, DataQuery, GrafanaTheme2 } from '@grafana/data'; import { AnnotationQuery, DataQuery, TypedVariableModel, GrafanaTheme2 } from '@grafana/data';
import { stylesFactory, Themeable2, withTheme2 } from '@grafana/ui'; import { stylesFactory, Themeable2, withTheme2 } from '@grafana/ui';
import { StoreState } from '../../../../types'; import { StoreState } from '../../../../types';
import { getSubMenuVariables, getVariablesState } from '../../../variables/state/selectors'; import { getSubMenuVariables, getVariablesState } from '../../../variables/state/selectors';
import { VariableModel } from '../../../variables/types';
import { DashboardModel } from '../../state'; import { DashboardModel } from '../../state';
import { DashboardLink } from '../../state/DashboardModel'; import { DashboardLink } from '../../state/DashboardModel';
@ -22,7 +21,7 @@ interface OwnProps extends Themeable2 {
} }
interface ConnectedProps { interface ConnectedProps {
variables: VariableModel[]; variables: TypedVariableModel[];
} }
interface DispatchProps {} interface DispatchProps {}

View File

@ -1,17 +1,17 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { TypedVariableModel, VariableHide } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
import { PickerRenderer } from '../../../variables/pickers/PickerRenderer'; import { PickerRenderer } from '../../../variables/pickers/PickerRenderer';
import { VariableHide, VariableModel } from '../../../variables/types';
interface Props { interface Props {
variables: VariableModel[]; variables: TypedVariableModel[];
readOnly?: boolean; readOnly?: boolean;
} }
export const SubMenuItems = ({ variables, readOnly }: Props) => { export const SubMenuItems = ({ variables, readOnly }: Props) => {
const [visibleVariables, setVisibleVariables] = useState<VariableModel[]>([]); const [visibleVariables, setVisibleVariables] = useState<TypedVariableModel[]>([]);
useEffect(() => { useEffect(() => {
setVisibleVariables(variables.filter((state) => state.hide !== VariableHide.hideVariable)); setVisibleVariables(variables.filter((state) => state.hide !== VariableHide.hideVariable));
@ -23,17 +23,15 @@ export const SubMenuItems = ({ variables, readOnly }: Props) => {
return ( return (
<> <>
{visibleVariables.map((variable) => { {visibleVariables.map((variable) => (
return ( <div
<div key={variable.id}
key={variable.id} className="submenu-item gf-form-inline"
className="submenu-item gf-form-inline" data-testid={selectors.pages.Dashboard.SubMenu.submenuItem}
data-testid={selectors.pages.Dashboard.SubMenu.submenuItem} >
> <PickerRenderer variable={variable} readOnly={readOnly} />
<PickerRenderer variable={variable} readOnly={readOnly} /> </div>
</div> ))}
);
})}
</> </>
); );
}; };

View File

@ -1,4 +1,4 @@
import { dateTime, TimeRange } from '@grafana/data'; import { dateTime, TimeRange, TypedVariableModel } from '@grafana/data';
import { setDataSourceSrv, VariableInterpolation } from '@grafana/runtime'; import { setDataSourceSrv, VariableInterpolation } from '@grafana/runtime';
import { ConstantVariable, EmbeddedScene, SceneCanvasText, SceneVariableSet, TestVariable } from '@grafana/scenes'; import { ConstantVariable, EmbeddedScene, SceneCanvasText, SceneVariableSet, TestVariable } from '@grafana/scenes';
import { VariableFormatID } from '@grafana/schema'; import { VariableFormatID } from '@grafana/schema';
@ -9,15 +9,14 @@ import { mockDataSource, MockDataSourceSrv } from '../alerting/unified/mocks';
import { VariableAdapter, variableAdapters } from '../variables/adapters'; import { VariableAdapter, variableAdapters } from '../variables/adapters';
import { createAdHocVariableAdapter } from '../variables/adhoc/adapter'; import { createAdHocVariableAdapter } from '../variables/adhoc/adapter';
import { createQueryVariableAdapter } from '../variables/query/adapter'; import { createQueryVariableAdapter } from '../variables/query/adapter';
import { VariableModel } from '../variables/types';
import { TemplateSrv } from './template_srv'; import { TemplateSrv } from './template_srv';
const key = 'key'; const key = 'key';
variableAdapters.setInit(() => [ variableAdapters.setInit(() => [
createQueryVariableAdapter() as unknown as VariableAdapter<VariableModel>, createQueryVariableAdapter() as unknown as VariableAdapter<TypedVariableModel>,
createAdHocVariableAdapter() as unknown as VariableAdapter<VariableModel>, createAdHocVariableAdapter() as unknown as VariableAdapter<TypedVariableModel>,
]); ]);
const interpolateMock = jest.fn(); const interpolateMock = jest.fn();

View File

@ -1,14 +1,13 @@
import { ComponentType } from 'react'; import { ComponentType } from 'react';
import { Reducer } from 'redux'; import { Reducer } from 'redux';
import { Registry, UrlQueryValue, VariableType } from '@grafana/data'; import { Registry, TypedVariableModel, UrlQueryValue, VariableOption, VariableType } from '@grafana/data';
import { VariableEditorProps } from './editor/types'; import { VariableEditorProps } from './editor/types';
import { VariablePickerProps } from './pickers/types'; import { VariablePickerProps } from './pickers/types';
import { VariablesState } from './state/types'; import { VariablesState } from './state/types';
import { VariableModel, VariableOption } from './types';
export interface VariableAdapter<Model extends VariableModel> { export interface VariableAdapter<Model extends TypedVariableModel> {
id: VariableType; id: VariableType;
description: string; description: string;
name: string; name: string;

View File

@ -2,6 +2,7 @@ import { css } from '@emotion/css';
import React, { ReactElement } from 'react'; import React, { ReactElement } from 'react';
import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd'; import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd';
import { TypedVariableModel } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
import { reportInteraction } from '@grafana/runtime'; import { reportInteraction } from '@grafana/runtime';
import { Button, useStyles2, Stack } from '@grafana/ui'; import { Button, useStyles2, Stack } from '@grafana/ui';
@ -10,12 +11,11 @@ import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
import { VariablesDependenciesButton } from '../inspect/VariablesDependenciesButton'; import { VariablesDependenciesButton } from '../inspect/VariablesDependenciesButton';
import { UsagesToNetwork, VariableUsageTree } from '../inspect/utils'; import { UsagesToNetwork, VariableUsageTree } from '../inspect/utils';
import { KeyedVariableIdentifier } from '../state/types'; import { KeyedVariableIdentifier } from '../state/types';
import { VariableModel } from '../types';
import { VariableEditorListRow } from './VariableEditorListRow'; import { VariableEditorListRow } from './VariableEditorListRow';
export interface Props { export interface Props {
variables: VariableModel[]; variables: TypedVariableModel[];
usages: VariableUsageTree[]; usages: VariableUsageTree[];
usagesNetwork: UsagesToNetwork[]; usagesNetwork: UsagesToNetwork[];
onAdd: () => void; onAdd: () => void;

View File

@ -2,21 +2,20 @@ import { css } from '@emotion/css';
import React, { ReactElement } from 'react'; import React, { ReactElement } from 'react';
import { Draggable } from 'react-beautiful-dnd'; import { Draggable } from 'react-beautiful-dnd';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2, TypedVariableModel } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
import { reportInteraction } from '@grafana/runtime'; import { reportInteraction } from '@grafana/runtime';
import { Button, Icon, IconButton, useStyles2, useTheme2 } from '@grafana/ui'; import { Button, Icon, IconButton, useStyles2, useTheme2 } from '@grafana/ui';
import { hasOptions, isAdHoc, isQuery } from '../guard'; import { hasOptions } from '../guard';
import { VariableUsagesButton } from '../inspect/VariableUsagesButton'; import { VariableUsagesButton } from '../inspect/VariableUsagesButton';
import { getVariableUsages, UsagesToNetwork, VariableUsageTree } from '../inspect/utils'; import { getVariableUsages, UsagesToNetwork, VariableUsageTree } from '../inspect/utils';
import { KeyedVariableIdentifier } from '../state/types'; import { KeyedVariableIdentifier } from '../state/types';
import { VariableModel } from '../types';
import { toKeyedVariableIdentifier } from '../utils'; import { toKeyedVariableIdentifier } from '../utils';
export interface VariableEditorListRowProps { export interface VariableEditorListRowProps {
index: number; index: number;
variable: VariableModel; variable: TypedVariableModel;
usageTree: VariableUsageTree[]; usageTree: VariableUsageTree[];
usagesNetwork: UsagesToNetwork[]; usagesNetwork: UsagesToNetwork[];
onEdit: (identifier: KeyedVariableIdentifier) => void; onEdit: (identifier: KeyedVariableIdentifier) => void;
@ -37,7 +36,7 @@ export function VariableEditorListRow({
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
const definition = getDefinition(variable); const definition = getDefinition(variable);
const usages = getVariableUsages(variable.id, usageTree); const usages = getVariableUsages(variable.id, usageTree);
const passed = usages > 0 || isAdHoc(variable); const passed = usages > 0 || variable.type === 'adhoc';
const identifier = toKeyedVariableIdentifier(variable); const identifier = toKeyedVariableIdentifier(variable);
return ( return (
@ -81,7 +80,7 @@ export function VariableEditorListRow({
<td role="gridcell" className={styles.column}> <td role="gridcell" className={styles.column}>
<div className={styles.icons}> <div className={styles.icons}>
<VariableCheckIndicator passed={passed} /> <VariableCheckIndicator passed={passed} />
<VariableUsagesButton id={variable.id} isAdhoc={isAdHoc(variable)} usages={usagesNetwork} /> <VariableUsagesButton id={variable.id} isAdhoc={variable.type === 'adhoc'} usages={usagesNetwork} />
<IconButton <IconButton
onClick={(event) => { onClick={(event) => {
event.preventDefault(); event.preventDefault();
@ -113,9 +112,9 @@ export function VariableEditorListRow({
); );
} }
function getDefinition(model: VariableModel): string { function getDefinition(model: TypedVariableModel): string {
let definition = ''; let definition = '';
if (isQuery(model)) { if (model.type === 'query') {
if (model.definition) { if (model.definition) {
definition = model.definition; definition = model.definition;
} else if (typeof model.query === 'string') { } else if (typeof model.query === 'string') {

View File

@ -1,6 +1,6 @@
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash';
import { VariableType } from '@grafana/data'; import { TypedVariableModel, VariableType } from '@grafana/data';
import { locationService } from '@grafana/runtime'; import { locationService } from '@grafana/runtime';
import { ThunkResult } from '../../../types'; import { ThunkResult } from '../../../types';
@ -11,7 +11,6 @@ import { toKeyedAction } from '../state/keyedVariablesReducer';
import { getEditorVariables, getNewVariableIndex, getVariable, getVariablesByKey } from '../state/selectors'; import { getEditorVariables, getNewVariableIndex, getVariable, getVariablesByKey } from '../state/selectors';
import { addVariable, removeVariable } from '../state/sharedReducer'; import { addVariable, removeVariable } from '../state/sharedReducer';
import { AddVariable, KeyedVariableIdentifier, VariableIdentifier } from '../state/types'; import { AddVariable, KeyedVariableIdentifier, VariableIdentifier } from '../state/types';
import { VariableModel } from '../types';
import { toKeyedVariableIdentifier, toStateKey, toVariablePayload } from '../utils'; import { toKeyedVariableIdentifier, toStateKey, toVariablePayload } from '../utils';
import { import {
@ -95,7 +94,7 @@ export const createNewVariable =
const identifier: VariableIdentifier = { type, id }; const identifier: VariableIdentifier = { type, id };
const global = false; const global = false;
const index = getNewVariableIndex(rootStateKey, getState()); const index = getNewVariableIndex(rootStateKey, getState());
const model: VariableModel = cloneDeep(variableAdapters.get(type).initialState); const model: TypedVariableModel = cloneDeep(variableAdapters.get(type).initialState);
model.id = id; model.id = id;
model.name = id; model.name = id;
model.rootStateKey = rootStateKey; model.rootStateKey = rootStateKey;
@ -119,7 +118,7 @@ export const initListMode =
dispatch(toKeyedAction(rootStateKey, initInspect({ usages, usagesNetwork }))); dispatch(toKeyedAction(rootStateKey, initInspect({ usages, usagesNetwork })));
}; };
export function getNextAvailableId(type: VariableType, variables: VariableModel[]): string { export function getNextAvailableId(type: VariableType, variables: TypedVariableModel[]): string {
let counter = 0; let counter = 0;
let nextId = `${type}${counter}`; let nextId = `${type}${counter}`;

View File

@ -1,14 +1,14 @@
import React, { PropsWithChildren, ReactElement, useMemo } from 'react'; import React, { PropsWithChildren, ReactElement, useMemo } from 'react';
import { TypedVariableModel, VariableHide } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
import { Tooltip } from '@grafana/ui'; import { Tooltip } from '@grafana/ui';
import { variableAdapters } from '../adapters'; import { variableAdapters } from '../adapters';
import { VARIABLE_PREFIX } from '../constants'; import { VARIABLE_PREFIX } from '../constants';
import { VariableHide, VariableModel } from '../types';
interface Props { interface Props {
variable: VariableModel; variable: TypedVariableModel;
readOnly?: boolean; readOnly?: boolean;
} }

View File

@ -1,8 +1,6 @@
import { VariableModel } from '@grafana/data'; import { TypedVariableModel, VariableWithOptions } from '@grafana/data';
import { VariableWithOptions } from '../types'; export const formatVariableLabel = (variable: VariableWithOptions | TypedVariableModel) => {
export const formatVariableLabel = (variable: VariableModel) => {
if (!isVariableWithOptions(variable)) { if (!isVariableWithOptions(variable)) {
return variable.name; return variable.name;
} }
@ -16,7 +14,7 @@ export const formatVariableLabel = (variable: VariableModel) => {
return current.text; return current.text;
}; };
const isVariableWithOptions = (variable: VariableModel): variable is VariableWithOptions => { const isVariableWithOptions = (variable: unknown): variable is VariableWithOptions => {
return ( return (
Array.isArray((variable as VariableWithOptions)?.options) || Array.isArray((variable as VariableWithOptions)?.options) ||
typeof (variable as VariableWithOptions)?.current === 'object' typeof (variable as VariableWithOptions)?.current === 'object'

View File

@ -10,6 +10,14 @@ import {
TypedVariableModel, TypedVariableModel,
UrlQueryMap, UrlQueryMap,
UrlQueryValue, UrlQueryValue,
OrgVariableModel,
QueryVariableModel,
DashboardVariableModel,
UserVariableModel,
VariableHide,
VariableOption,
VariableRefresh,
VariableWithOptions,
} from '@grafana/data'; } from '@grafana/data';
import { config, locationService } from '@grafana/runtime'; import { config, locationService } from '@grafana/runtime';
import { notifyApp } from 'app/core/actions'; import { notifyApp } from 'app/core/actions';
@ -28,35 +36,17 @@ import { getTemplateSrv, TemplateSrv } from '../../templating/template_srv';
import { variableAdapters } from '../adapters'; import { variableAdapters } from '../adapters';
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, VARIABLE_PREFIX } from '../constants'; import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, VARIABLE_PREFIX } from '../constants';
import { cleanEditorState } from '../editor/reducer'; import { cleanEditorState } from '../editor/reducer';
import { import { hasCurrent, hasLegacyVariableSupport, hasOptions, hasStandardVariableSupport, isMulti } from '../guard';
hasCurrent,
hasLegacyVariableSupport,
hasOptions,
hasStandardVariableSupport,
isAdHoc,
isConstant,
isMulti,
isQuery,
} from '../guard';
import { getAllAffectedPanelIdsForVariableChange, getPanelVars } from '../inspect/utils'; import { getAllAffectedPanelIdsForVariableChange, getPanelVars } from '../inspect/utils';
import { cleanPickerState } from '../pickers/OptionsPicker/reducer'; import { cleanPickerState } from '../pickers/OptionsPicker/reducer';
import { alignCurrentWithMulti } from '../shared/multiOptions'; import { alignCurrentWithMulti } from '../shared/multiOptions';
import { import {
DashboardVariableModel,
initialVariableModelState, initialVariableModelState,
OrgVariableModel,
QueryVariableModel,
TransactionStatus, TransactionStatus,
UserVariableModel,
VariableHide,
VariableModel,
VariableOption,
VariableRefresh,
VariablesChanged, VariablesChanged,
VariablesChangedEvent, VariablesChangedEvent,
VariablesChangedInUrl, VariablesChangedInUrl,
VariablesTimeRangeProcessDone, VariablesTimeRangeProcessDone,
VariableWithOptions,
} from '../types'; } from '../types';
import { import {
ensureStringValues, ensureStringValues,
@ -145,7 +135,7 @@ export const initDashboardTemplating = (key: string, dashboard: DashboardModel):
}; };
}; };
export function fixSelectedInconsistency(model: VariableModel): VariableModel | VariableWithOptions { export function fixSelectedInconsistency(model: TypedVariableModel): TypedVariableModel | VariableWithOptions {
if (!hasOptions(model)) { if (!hasOptions(model)) {
return model; return model;
} }
@ -275,7 +265,7 @@ export const changeVariableMultiValue = (identifier: KeyedVariableIdentifier, mu
}; };
}; };
export const processVariableDependencies = async (variable: VariableModel, state: StoreState) => { export const processVariableDependencies = async (variable: TypedVariableModel, state: StoreState) => {
if (!variable.rootStateKey) { if (!variable.rootStateKey) {
throw new Error(`rootStateKey not found for variable with id:${variable.id}`); throw new Error(`rootStateKey not found for variable with id:${variable.id}`);
} }
@ -305,7 +295,7 @@ export const processVariableDependencies = async (variable: VariableModel, state
}; };
const isDependencyGraphCircular = ( const isDependencyGraphCircular = (
variable: VariableModel, variable: TypedVariableModel,
state: StoreState, state: StoreState,
encounteredDependencyIds: Set<string> = new Set() encounteredDependencyIds: Set<string> = new Set()
): boolean => { ): boolean => {
@ -320,12 +310,12 @@ const isDependencyGraphCircular = (
}); });
}; };
const getDirectDependencies = (variable: VariableModel, state: StoreState) => { const getDirectDependencies = (variable: TypedVariableModel, state: StoreState) => {
if (!variable.rootStateKey) { if (!variable.rootStateKey) {
return []; return [];
} }
const directDependencies: VariableModel[] = []; const directDependencies: TypedVariableModel[] = [];
for (const otherVariable of getVariablesByKey(variable.rootStateKey, state)) { for (const otherVariable of getVariablesByKey(variable.rootStateKey, state)) {
if (variable === otherVariable) { if (variable === otherVariable) {
@ -342,7 +332,7 @@ const getDirectDependencies = (variable: VariableModel, state: StoreState) => {
return directDependencies; return directDependencies;
}; };
const isWaitingForDependencies = (key: string, dependencies: VariableModel[], state: StoreState): boolean => { const isWaitingForDependencies = (key: string, dependencies: TypedVariableModel[], state: StoreState): boolean => {
if (dependencies.length === 0) { if (dependencies.length === 0) {
return false; return false;
} }
@ -395,8 +385,7 @@ export const processVariables = (key: string): ThunkResult<Promise<void>> => {
return async (dispatch, getState) => { return async (dispatch, getState) => {
const queryParams = locationService.getSearchObject(); const queryParams = locationService.getSearchObject();
const promises = getVariablesByKey(key, getState()).map( const promises = getVariablesByKey(key, getState()).map(
async (variable: VariableModel) => async (variable) => await dispatch(processVariable(toKeyedVariableIdentifier(variable), queryParams))
await dispatch(processVariable(toKeyedVariableIdentifier(variable), queryParams))
); );
await Promise.all(promises); await Promise.all(promises);
@ -571,7 +560,7 @@ export const setOptionAsCurrent = (
}; };
}; };
export const createGraph = (variables: VariableModel[]) => { export const createGraph = (variables: TypedVariableModel[]) => {
const g = new Graph(); const g = new Graph();
variables.forEach((v) => { variables.forEach((v) => {
@ -618,13 +607,14 @@ export const variableUpdated = (
const panels = state.dashboard?.getModel()?.panels ?? []; const panels = state.dashboard?.getModel()?.panels ?? [];
const panelVars = getPanelVars(panels); const panelVars = getPanelVars(panels);
const event: VariablesChangedEvent = isAdHoc(variableInState) const event: VariablesChangedEvent =
? { refreshAll: true, panelIds: [] } // for adhoc variables we don't know which panels that will be impacted variableInState.type === 'adhoc'
: { ? { refreshAll: true, panelIds: [] } // for adhoc variables we don't know which panels that will be impacted
refreshAll: false, : {
panelIds: Array.from(getAllAffectedPanelIdsForVariableChange([variableInState.id], g, panelVars)), refreshAll: false,
variable: getVariable(identifier, state), panelIds: Array.from(getAllAffectedPanelIdsForVariableChange([variableInState.id], g, panelVars)),
}; variable: getVariable(identifier, state),
};
const node = g.getNode(variableInState.name); const node = g.getNode(variableInState.name);
let promises: Array<Promise<any>> = []; let promises: Array<Promise<any>> = [];
@ -653,7 +643,12 @@ export interface OnTimeRangeUpdatedDependencies {
events: typeof appEvents; events: typeof appEvents;
} }
const dfs = (node: Node, visited: string[], variables: VariableModel[], variablesRefreshTimeRange: VariableModel[]) => { const dfs = (
node: Node,
visited: string[],
variables: TypedVariableModel[],
variablesRefreshTimeRange: TypedVariableModel[]
) => {
if (!visited.includes(node.name)) { if (!visited.includes(node.name)) {
visited.push(node.name); visited.push(node.name);
} }
@ -858,7 +853,7 @@ export const templateVarsChangedInUrl =
value = variableInModel.current.value; // revert value to the value stored in dashboard json value = variableInModel.current.value; // revert value to the value stored in dashboard json
} }
if (variableInModel && isConstant(variableInModel)) { if (variableInModel && variableInModel.type === 'constant') {
value = variableInModel.query; // revert value to the value stored in dashboard json, constants don't store current values in dashboard json value = variableInModel.query; // revert value to the value stored in dashboard json, constants don't store current values in dashboard json
} }
} }
@ -869,7 +864,9 @@ export const templateVarsChangedInUrl =
const filteredVars = variables.filter((v) => { const filteredVars = variables.filter((v) => {
const key = VARIABLE_PREFIX + v.name; const key = VARIABLE_PREFIX + v.name;
return vars.hasOwnProperty(key) && isVariableUrlValueDifferentFromCurrent(v, vars[key].value) && !isAdHoc(v); return (
vars.hasOwnProperty(key) && isVariableUrlValueDifferentFromCurrent(v, vars[key].value) && v.type !== 'adhoc'
);
}); });
const varGraph = createGraph(variables); const varGraph = createGraph(variables);
const panelVars = getPanelVars(dashboard?.panels ?? []); const panelVars = getPanelVars(dashboard?.panels ?? []);
@ -891,7 +888,7 @@ export const templateVarsChangedInUrl =
} }
}; };
export function isVariableUrlValueDifferentFromCurrent(variable: VariableModel, urlValue: any): boolean { export function isVariableUrlValueDifferentFromCurrent(variable: TypedVariableModel, urlValue: any): boolean {
const variableValue = variableAdapters.get(variable.type).getValueForUrl(variable); const variableValue = variableAdapters.get(variable.type).getValueForUrl(variable);
let stringUrlValue = ensureStringValues(urlValue); let stringUrlValue = ensureStringValues(urlValue);
if (Array.isArray(variableValue) && !Array.isArray(stringUrlValue)) { if (Array.isArray(variableValue) && !Array.isArray(stringUrlValue)) {
@ -963,7 +960,7 @@ export function migrateVariablesDatasourceNameToRef(
return (dispatch, getState) => { return (dispatch, getState) => {
const variables = getVariablesByKey(key, getState()); const variables = getVariablesByKey(key, getState());
for (const variable of variables) { for (const variable of variables) {
if (!isAdHoc(variable) && !isQuery(variable)) { if (variable.type !== 'adhoc' && variable.type !== 'query') {
continue; continue;
} }

View File

@ -1,20 +1,19 @@
import { combineReducers } from '@reduxjs/toolkit'; import { combineReducers } from '@reduxjs/toolkit';
import { TypedVariableModel } from '@grafana/data'; import {
TypedVariableModel,
DashboardVariableModel,
OrgVariableModel,
UserVariableModel,
VariableHide,
} from '@grafana/data';
import { VariableRefresh } from '@grafana/schema'; import { VariableRefresh } from '@grafana/schema';
import { dashboardReducer } from 'app/features/dashboard/state/reducers'; import { dashboardReducer } from 'app/features/dashboard/state/reducers';
import { DashboardState, StoreState } from '../../../types'; import { DashboardState, StoreState } from '../../../types';
import { VariableAdapter } from '../adapters'; import { VariableAdapter } from '../adapters';
import { NEW_VARIABLE_ID } from '../constants'; import { NEW_VARIABLE_ID } from '../constants';
import { import { initialVariableModelState } from '../types';
DashboardVariableModel,
initialVariableModelState,
OrgVariableModel,
UserVariableModel,
VariableHide,
VariableModel,
} from '../types';
import { createQueryVariable } from './__tests__/fixtures'; import { createQueryVariable } from './__tests__/fixtures';
import { keyedVariablesReducer, KeyedVariablesState } from './keyedVariablesReducer'; import { keyedVariablesReducer, KeyedVariablesState } from './keyedVariablesReducer';
@ -112,7 +111,7 @@ export const getVariableTestContext = <Model extends TypedVariableModel>(
adapter: VariableAdapter<Model>, adapter: VariableAdapter<Model>,
variableOverrides: Partial<Model> = {} variableOverrides: Partial<Model> = {}
) => { ) => {
const defaults: Partial<VariableModel> = { const defaults: Partial<TypedVariableModel> = {
id: '0', id: '0',
rootStateKey: 'key', rootStateKey: 'key',
index: 0, index: 0,

View File

@ -1,12 +1,11 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { cloneDeep, defaults as lodashDefaults } from 'lodash'; import { cloneDeep, defaults as lodashDefaults } from 'lodash';
import { LoadingState, VariableType } from '@grafana/data'; import { LoadingState, VariableType, TypedVariableModel, VariableOption } from '@grafana/data';
import { variableAdapters } from '../adapters'; import { variableAdapters } from '../adapters';
import { changeVariableNameSucceeded } from '../editor/reducer'; import { changeVariableNameSucceeded } from '../editor/reducer';
import { hasOptions } from '../guard'; import { hasOptions } from '../guard';
import { VariableModel, VariableOption } from '../types';
import { ensureStringValues } from '../utils'; import { ensureStringValues } from '../utils';
import { getInstanceState, getNextVariableIndex } from './selectors'; import { getInstanceState, getNextVariableIndex } from './selectors';
@ -77,7 +76,7 @@ const sharedReducerSlice = createSlice({
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
} }
const original = cloneDeep<VariableModel>(state[action.payload.id]); const original = cloneDeep<TypedVariableModel>(state[action.payload.id]);
const copyRegex = new RegExp(`^copy_of_${escapeRegExp(original.name)}(_(\\d+))?$`); const copyRegex = new RegExp(`^copy_of_${escapeRegExp(original.name)}(_(\\d+))?$`);
const copies = Object.values(state) const copies = Object.values(state)

View File

@ -1,6 +1,15 @@
import { isArray, isEqual } from 'lodash'; import { isArray, isEqual } from 'lodash';
import { LegacyMetricFindQueryOptions, ScopedVars, UrlQueryMap, UrlQueryValue, VariableType } from '@grafana/data'; import {
LegacyMetricFindQueryOptions,
ScopedVars,
UrlQueryMap,
UrlQueryValue,
VariableType,
VariableRefresh,
VariableWithOptions,
QueryVariableModel,
} from '@grafana/data';
import { getTemplateSrv } from '@grafana/runtime'; import { getTemplateSrv } from '@grafana/runtime';
import { safeStringifyValue } from 'app/core/utils/explore'; import { safeStringifyValue } from 'app/core/utils/explore';
@ -12,7 +21,7 @@ import { variableAdapters } from './adapters';
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, VARIABLE_PREFIX } from './constants'; import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, VARIABLE_PREFIX } from './constants';
import { getVariablesState } from './state/selectors'; import { getVariablesState } from './state/selectors';
import { KeyedVariableIdentifier, VariableIdentifier, VariablePayload } from './state/types'; import { KeyedVariableIdentifier, VariableIdentifier, VariablePayload } from './state/types';
import { QueryVariableModel, TransactionStatus, VariableModel, VariableRefresh, VariableWithOptions } from './types'; import { TransactionStatus, VariableModel } from './types';
/* /*
* This regex matches 3 types of variable reference with an optional format specifier * This regex matches 3 types of variable reference with an optional format specifier

View File

@ -1,4 +1,4 @@
import { VariableModel } from '@grafana/data'; import { TypedVariableModel } from '@grafana/data';
import { EngineSchema } from '../types'; import { EngineSchema } from '../types';
import { AzureLogAnalyticsMetadata } from '../types/logAnalyticsMetadata'; import { AzureLogAnalyticsMetadata } from '../types/logAnalyticsMetadata';
@ -48,7 +48,7 @@ function transformMetadataFunction(sourceSchema: AzureLogAnalyticsMetadata) {
export function transformMetadataToKustoSchema( export function transformMetadataToKustoSchema(
sourceSchema: AzureLogAnalyticsMetadata, sourceSchema: AzureLogAnalyticsMetadata,
nameOrIdOrSomething: string, nameOrIdOrSomething: string,
templateVariables: VariableModel[] templateVariables: TypedVariableModel[]
): EngineSchema { ): EngineSchema {
const database = { const database = {
name: nameOrIdOrSomething, name: nameOrIdOrSomething,