mirror of
https://github.com/grafana/grafana.git
synced 2025-02-09 23:16:16 -06:00
Variables: migrates data source variable type to React/Redux (#22770)
* Refactor: moves all the newVariables part to features/variables directory * Feature: adds datasource type * Tests: adds reducer tests * Tests: covers data source actions with tests * Chore: reduces strict null errors
This commit is contained in:
parent
b30f4c7bb0
commit
1db067396a
@ -28,7 +28,7 @@ import {
|
||||
SaveDashboardAsButtonConnected,
|
||||
SaveDashboardButtonConnected,
|
||||
} from '../features/dashboard/components/SaveDashboard/SaveDashboardButton';
|
||||
import { VariableEditorContainer } from '../features/templating/editor/VariableEditorContainer';
|
||||
import { VariableEditorContainer } from '../features/variables/editor/VariableEditorContainer';
|
||||
|
||||
export function registerAngularDirectives() {
|
||||
react2AngularDirective('footer', Footer, []);
|
||||
|
@ -13,7 +13,7 @@ import usersReducers from 'app/features/users/state/reducers';
|
||||
import userReducers from 'app/features/profile/state/reducers';
|
||||
import organizationReducers from 'app/features/org/state/reducers';
|
||||
import ldapReducers from 'app/features/admin/state/reducers';
|
||||
import templatingReducers from 'app/features/templating/state/reducers';
|
||||
import templatingReducers from 'app/features/variables/state/reducers';
|
||||
|
||||
const rootReducers = {
|
||||
...sharedReducers,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { connect, MapStateToProps } from 'react-redux';
|
||||
import { StoreState } from '../../../../types';
|
||||
import { getVariableClones } from '../../../templating/state/selectors';
|
||||
import { getVariableClones } from '../../../variables/state/selectors';
|
||||
import { VariableHide, VariableModel } from '../../../templating/variable';
|
||||
import { DashboardModel } from '../../state';
|
||||
import { AngularDashboardLinks } from './AngularDashboardLinks';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { FunctionComponent, useEffect, useState } from 'react';
|
||||
import { VariableHide, VariableModel } from '../../../templating/variable';
|
||||
import { e2e } from '@grafana/e2e';
|
||||
import { PickerRenderer } from '../../../templating/pickers/PickerRenderer';
|
||||
import { PickerRenderer } from '../../../variables/pickers/PickerRenderer';
|
||||
|
||||
interface Props {
|
||||
variables: VariableModel[];
|
||||
|
@ -15,8 +15,8 @@ import { UrlQueryValue } from '@grafana/runtime';
|
||||
import { CoreEvents, DashboardMeta, KIOSK_MODE_TV } from 'app/types';
|
||||
import { VariableModel } from '../../templating/variable';
|
||||
import { getConfig } from '../../../core/config';
|
||||
import { getVariables } from 'app/features/templating/state/selectors';
|
||||
import { variableAdapters } from 'app/features/templating/adapters';
|
||||
import { getVariables } from 'app/features/variables/state/selectors';
|
||||
import { variableAdapters } from 'app/features/variables/adapters';
|
||||
|
||||
export interface CloneOptions {
|
||||
saveVariables?: boolean;
|
||||
|
@ -23,8 +23,8 @@ import { DashboardDTO, DashboardRouteInfo, StoreState, ThunkDispatch, ThunkResul
|
||||
import { DashboardModel } from './DashboardModel';
|
||||
import { DataQuery } from '@grafana/data';
|
||||
import { getConfig } from '../../../core/config';
|
||||
import { initDashboardTemplating, processVariables } from '../../templating/state/actions';
|
||||
import { variableAdapters } from '../../templating/adapters';
|
||||
import { initDashboardTemplating, processVariables } from '../../variables/state/actions';
|
||||
import { variableAdapters } from '../../variables/adapters';
|
||||
|
||||
export interface InitDashboardArgs {
|
||||
$injector: any;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { coreModule } from 'app/core/core';
|
||||
import { VariableSrv } from 'app/features/templating/variable_srv';
|
||||
import { getConfig } from '../../core/config';
|
||||
import { getVariables } from '../templating/state/selectors';
|
||||
import { getVariables } from '../variables/state/selectors';
|
||||
|
||||
const template = `
|
||||
<div class="gf-form-select-wrapper max-width-18">
|
||||
|
@ -10,11 +10,12 @@ import { CustomVariable } from './custom_variable';
|
||||
import { ConstantVariable } from './constant_variable';
|
||||
import { AdhocVariable } from './adhoc_variable';
|
||||
import { TextBoxVariable } from './TextBoxVariable';
|
||||
import { variableAdapters } from './adapters';
|
||||
import { createQueryVariableAdapter } from './query/adapter';
|
||||
import { createCustomVariableAdapter } from './custom/adapter';
|
||||
import { createTextBoxVariableAdapter } from './textbox/adapter';
|
||||
import { createConstantVariableAdapter } from './constant/adapter';
|
||||
import { variableAdapters } from '../variables/adapters';
|
||||
import { createQueryVariableAdapter } from '../variables/query/adapter';
|
||||
import { createCustomVariableAdapter } from '../variables/custom/adapter';
|
||||
import { createTextBoxVariableAdapter } from '../variables/textbox/adapter';
|
||||
import { createConstantVariableAdapter } from '../variables/constant/adapter';
|
||||
import { createDataSourceVariableAdapter } from '../variables/datasource/adapter';
|
||||
|
||||
coreModule.factory('templateSrv', () => templateSrv);
|
||||
|
||||
@ -33,3 +34,4 @@ variableAdapters.set('query', createQueryVariableAdapter());
|
||||
variableAdapters.set('custom', createCustomVariableAdapter());
|
||||
variableAdapters.set('textbox', createTextBoxVariableAdapter());
|
||||
variableAdapters.set('constant', createConstantVariableAdapter());
|
||||
variableAdapters.set('datasource', createDataSourceVariableAdapter());
|
||||
|
@ -3,7 +3,7 @@ import _ from 'lodash';
|
||||
import { variableRegex } from 'app/features/templating/variable';
|
||||
import { escapeHtml } from 'app/core/utils/text';
|
||||
import { ScopedVars, TimeRange } from '@grafana/data';
|
||||
import { getVariableWithName } from './state/selectors';
|
||||
import { getVariableWithName } from '../variables/state/selectors';
|
||||
import { getState } from '../../store/store';
|
||||
import { getConfig } from 'app/core/config';
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { ComponentType } from 'react';
|
||||
import { Reducer } from 'redux';
|
||||
import { UrlQueryValue } from '@grafana/runtime';
|
||||
|
||||
import { VariableModel, VariableOption, VariableType } from './variable';
|
||||
import { VariableModel, VariableOption, VariableType } from '../templating/variable';
|
||||
import { VariableEditorProps } from './editor/types';
|
||||
import { VariablesState } from './state/variablesReducer';
|
||||
import { VariablePickerProps } from './pickers/types';
|
@ -1,7 +1,7 @@
|
||||
import React, { ChangeEvent, FocusEvent, PureComponent } from 'react';
|
||||
import { e2e } from '@grafana/e2e';
|
||||
|
||||
import { ConstantVariableModel } from '../variable';
|
||||
import { ConstantVariableModel } from '../../templating/variable';
|
||||
import { VariableEditorProps } from '../editor/types';
|
||||
|
||||
export interface Props extends VariableEditorProps<ConstantVariableModel> {}
|
@ -1,10 +1,10 @@
|
||||
import { variableAdapters } from '../adapters';
|
||||
import { createConstantVariableAdapter } from '../constant/adapter';
|
||||
import { createConstantVariableAdapter } from './adapter';
|
||||
import { reduxTester } from '../../../../test/core/redux/reduxTester';
|
||||
import { TemplatingState } from 'app/features/templating/state/reducers';
|
||||
import { TemplatingState } from 'app/features/variables/state/reducers';
|
||||
import { updateConstantVariableOptions } from './actions';
|
||||
import { getTemplatingRootReducer } from '../state/helpers';
|
||||
import { ConstantVariableModel, VariableOption, VariableHide } from '../variable';
|
||||
import { ConstantVariableModel, VariableHide, VariableOption } from '../../templating/variable';
|
||||
import { toVariablePayload } from '../state/types';
|
||||
import { createConstantOptionsFromQuery } from './reducer';
|
||||
import { setCurrentVariableValue } from '../state/sharedReducer';
|
@ -1,5 +1,5 @@
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { ConstantVariableModel } from '../variable';
|
||||
import { ConstantVariableModel } from '../../templating/variable';
|
||||
import { dispatch } from '../../../store/store';
|
||||
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions';
|
||||
import { VariableAdapter } from '../adapters';
|
@ -4,7 +4,7 @@ import { getVariableTestContext } from '../state/helpers';
|
||||
import { toVariablePayload } from '../state/types';
|
||||
import { constantVariableReducer, createConstantOptionsFromQuery } from './reducer';
|
||||
import { VariablesState } from '../state/variablesReducer';
|
||||
import { ConstantVariableModel } from '../variable';
|
||||
import { ConstantVariableModel } from '../../templating/variable';
|
||||
import { createConstantVariableAdapter } from './adapter';
|
||||
|
||||
describe('constantVariableReducer', () => {
|
@ -1,5 +1,5 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { ConstantVariableModel, VariableHide, VariableOption } from '../variable';
|
||||
import { ConstantVariableModel, VariableHide, VariableOption } from '../../templating/variable';
|
||||
import { EMPTY_UUID, getInstanceState, VariablePayload } from '../state/types';
|
||||
import { initialVariablesState, VariablesState } from '../state/variablesReducer';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { ChangeEvent, FocusEvent, PureComponent } from 'react';
|
||||
import { CustomVariableModel, VariableWithMultiSupport } from '../variable';
|
||||
import { CustomVariableModel, VariableWithMultiSupport } from '../../templating/variable';
|
||||
import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor';
|
||||
import { OnPropChangeArguments, VariableEditorProps } from '../editor/types';
|
||||
|
@ -3,7 +3,7 @@ import { updateCustomVariableOptions } from './actions';
|
||||
import { createCustomVariableAdapter } from './adapter';
|
||||
import { reduxTester } from '../../../../test/core/redux/reduxTester';
|
||||
import { getTemplatingRootReducer } from '../state/helpers';
|
||||
import { VariableOption, VariableHide, CustomVariableModel } from '../variable';
|
||||
import { CustomVariableModel, VariableHide, VariableOption } from '../../templating/variable';
|
||||
import { toVariablePayload } from '../state/types';
|
||||
import { setCurrentVariableValue } from '../state/sharedReducer';
|
||||
import { initDashboardTemplating } from '../state/actions';
|
@ -1,5 +1,5 @@
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { CustomVariableModel } from '../variable';
|
||||
import { CustomVariableModel } from '../../templating/variable';
|
||||
import { dispatch } from '../../../store/store';
|
||||
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions';
|
||||
import { VariableAdapter } from '../adapters';
|
@ -1,11 +1,11 @@
|
||||
import { reducerTester } from '../../../../test/core/redux/reducerTester';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { getVariableTestContext } from '../state/helpers';
|
||||
import { toVariablePayload, ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from '../state/types';
|
||||
import { customVariableReducer, createCustomOptionsFromQuery } from './reducer';
|
||||
import { createCustomVariableAdapter } from '../custom/adapter';
|
||||
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, toVariablePayload } from '../state/types';
|
||||
import { createCustomOptionsFromQuery, customVariableReducer } from './reducer';
|
||||
import { createCustomVariableAdapter } from './adapter';
|
||||
import { VariablesState } from '../state/variablesReducer';
|
||||
import { CustomVariableModel } from '../variable';
|
||||
import { CustomVariableModel } from '../../templating/variable';
|
||||
|
||||
describe('customVariableReducer', () => {
|
||||
const adapter = createCustomVariableAdapter();
|
@ -1,6 +1,6 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
import { CustomVariableModel, VariableHide, VariableOption } from '../variable';
|
||||
import { CustomVariableModel, VariableHide, VariableOption } from '../../templating/variable';
|
||||
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, EMPTY_UUID, getInstanceState, VariablePayload } from '../state/types';
|
||||
import { initialVariablesState, VariablesState } from '../state/variablesReducer';
|
||||
|
@ -0,0 +1,131 @@
|
||||
import React, { ChangeEvent, FocusEvent, PureComponent } from 'react';
|
||||
|
||||
import { DataSourceVariableModel, VariableWithMultiSupport } from '../../templating/variable';
|
||||
import { OnPropChangeArguments, VariableEditorProps } from '../editor/types';
|
||||
import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor';
|
||||
import { FormLabel } from '@grafana/ui';
|
||||
import { VariableEditorState } from '../editor/reducer';
|
||||
import { DataSourceVariableEditorState } from './reducer';
|
||||
import { initDataSourceVariableEditor } from './actions';
|
||||
import { MapDispatchToProps, MapStateToProps } from 'react-redux';
|
||||
import { StoreState } from '../../../types';
|
||||
import { connectWithStore } from '../../../core/utils/connectWithReduxStore';
|
||||
|
||||
export interface OwnProps extends VariableEditorProps<DataSourceVariableModel> {}
|
||||
|
||||
interface ConnectedProps {
|
||||
editor: VariableEditorState<DataSourceVariableEditorState>;
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
initDataSourceVariableEditor: typeof initDataSourceVariableEditor;
|
||||
}
|
||||
|
||||
type Props = OwnProps & ConnectedProps & DispatchProps;
|
||||
|
||||
export class DataSourceVariableEditorUnConnected extends PureComponent<Props> {
|
||||
async componentDidMount() {
|
||||
await this.props.initDataSourceVariableEditor();
|
||||
}
|
||||
|
||||
onRegExChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
this.props.onPropChange({
|
||||
propName: 'regex',
|
||||
propValue: event.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
onRegExBlur = (event: FocusEvent<HTMLInputElement>) => {
|
||||
this.props.onPropChange({
|
||||
propName: 'regex',
|
||||
propValue: event.target.value,
|
||||
updateOptions: true,
|
||||
});
|
||||
};
|
||||
|
||||
onSelectionOptionsChange = async ({ propValue, propName }: OnPropChangeArguments<VariableWithMultiSupport>) => {
|
||||
this.props.onPropChange({ propName, propValue, updateOptions: true });
|
||||
};
|
||||
|
||||
getSelectedDataSourceTypeValue = (): string => {
|
||||
if (!this.props.editor.extended?.dataSourceTypes?.length) {
|
||||
return '';
|
||||
}
|
||||
const foundItem = this.props.editor.extended?.dataSourceTypes.find(ds => ds.value === this.props.variable.query);
|
||||
const value = foundItem ? foundItem.value : this.props.editor.extended?.dataSourceTypes[0].value;
|
||||
return value ?? '';
|
||||
};
|
||||
|
||||
onDataSourceTypeChanged = (event: ChangeEvent<HTMLSelectElement>) => {
|
||||
this.props.onPropChange({ propName: 'query', propValue: event.target.value, updateOptions: true });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<div className="gf-form-group">
|
||||
<h5 className="section-heading">Data source options</h5>
|
||||
|
||||
<div className="gf-form">
|
||||
<label className="gf-form-label width-12">Type</label>
|
||||
<div className="gf-form-select-wrapper max-width-18">
|
||||
<select
|
||||
className="gf-form-input"
|
||||
value={this.getSelectedDataSourceTypeValue()}
|
||||
onChange={this.onDataSourceTypeChanged}
|
||||
>
|
||||
{this.props.editor.extended?.dataSourceTypes?.length &&
|
||||
this.props.editor.extended?.dataSourceTypes?.map(ds => (
|
||||
<option key={ds.value ?? ''} value={ds.value ?? ''} label={ds.text}>
|
||||
{ds.text}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="gf-form">
|
||||
<FormLabel
|
||||
width={12}
|
||||
tooltip={
|
||||
<div>
|
||||
Regex filter for which data source instances to choose from in the variable value dropdown. Leave
|
||||
empty for all.
|
||||
<br />
|
||||
<br />
|
||||
Example: <code>/^prod/</code>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
Instance name filter
|
||||
</FormLabel>
|
||||
<input
|
||||
type="text"
|
||||
className="gf-form-input max-width-18"
|
||||
placeholder="/.*-(.*)-.*/"
|
||||
value={this.props.variable.regex}
|
||||
onChange={this.onRegExChange}
|
||||
onBlur={this.onRegExBlur}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SelectionOptionsEditor variable={this.props.variable} onPropChange={this.onSelectionOptionsChange} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps: MapStateToProps<ConnectedProps, OwnProps, StoreState> = (state, ownProps) => ({
|
||||
editor: state.templating.editor as VariableEditorState<DataSourceVariableEditorState>,
|
||||
});
|
||||
|
||||
const mapDispatchToProps: MapDispatchToProps<DispatchProps, OwnProps> = {
|
||||
initDataSourceVariableEditor,
|
||||
};
|
||||
|
||||
export const DataSourceVariableEditor = connectWithStore(
|
||||
DataSourceVariableEditorUnConnected,
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
);
|
175
public/app/features/variables/datasource/actions.test.ts
Normal file
175
public/app/features/variables/datasource/actions.test.ts
Normal file
@ -0,0 +1,175 @@
|
||||
import { reduxTester } from '../../../../test/core/redux/reduxTester';
|
||||
import { TemplatingState } from '../state/reducers';
|
||||
import { getTemplatingRootReducer, variableMockBuilder } from '../state/helpers';
|
||||
import { initDashboardTemplating } from '../state/actions';
|
||||
import { toVariableIdentifier, toVariablePayload } from '../state/types';
|
||||
import { variableAdapters } from '../adapters';
|
||||
import { createDataSourceVariableAdapter } from './adapter';
|
||||
import {
|
||||
DataSourceVariableActionDependencies,
|
||||
initDataSourceVariableEditor,
|
||||
updateDataSourceVariableOptions,
|
||||
} from './actions';
|
||||
import { DataSourcePluginMeta, DataSourceSelectItem } from '@grafana/data';
|
||||
import { getMockPlugin } from '../../plugins/__mocks__/pluginMocks';
|
||||
import { createDataSourceOptions } from './reducer';
|
||||
import { setCurrentVariableValue } from '../state/sharedReducer';
|
||||
import { changeVariableEditorExtended } from '../editor/reducer';
|
||||
|
||||
describe('data source actions', () => {
|
||||
variableAdapters.set('datasource', createDataSourceVariableAdapter());
|
||||
|
||||
describe('when updateDataSourceVariableOptions is dispatched', () => {
|
||||
describe('and there is no regex', () => {
|
||||
it('then the correct actions are dispatched', async () => {
|
||||
const sources: DataSourceSelectItem[] = [
|
||||
{
|
||||
name: 'first-name',
|
||||
value: 'first-value',
|
||||
meta: getMockPlugin({ name: 'mock-data-name', id: 'mock-data-id' }),
|
||||
sort: '',
|
||||
},
|
||||
{
|
||||
name: 'second-name',
|
||||
value: 'second-value',
|
||||
meta: getMockPlugin({ name: 'mock-data-name', id: 'mock-data-id' }),
|
||||
sort: '',
|
||||
},
|
||||
];
|
||||
|
||||
const getMetricSourcesMock = jest.fn().mockResolvedValue(sources);
|
||||
const getDatasourceSrvMock = jest.fn().mockReturnValue({ getMetricSources: getMetricSourcesMock });
|
||||
const dependencies: DataSourceVariableActionDependencies = { getDatasourceSrv: getDatasourceSrvMock };
|
||||
const datasource = variableMockBuilder('datasource')
|
||||
.withUuid('0')
|
||||
.withQuery('mock-data-id')
|
||||
.create();
|
||||
const tester = await reduxTester<{ templating: TemplatingState }>()
|
||||
.givenRootReducer(getTemplatingRootReducer())
|
||||
.whenActionIsDispatched(initDashboardTemplating([datasource]))
|
||||
.whenAsyncActionIsDispatched(
|
||||
updateDataSourceVariableOptions(toVariableIdentifier(datasource), dependencies),
|
||||
true
|
||||
);
|
||||
|
||||
await tester.thenDispatchedActionShouldEqual(
|
||||
createDataSourceOptions(
|
||||
toVariablePayload({ type: 'datasource', uuid: '0' }, { sources, regex: (undefined as unknown) as RegExp })
|
||||
),
|
||||
setCurrentVariableValue(
|
||||
toVariablePayload(
|
||||
{ type: 'datasource', uuid: '0' },
|
||||
{ option: { text: 'first-name', value: 'first-name', selected: false } }
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
expect(getMetricSourcesMock).toHaveBeenCalledTimes(1);
|
||||
expect(getMetricSourcesMock).toHaveBeenCalledWith({ skipVariables: true });
|
||||
expect(getDatasourceSrvMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and there is a regex', () => {
|
||||
it('then the correct actions are dispatched', async () => {
|
||||
const sources: DataSourceSelectItem[] = [
|
||||
{
|
||||
name: 'first-name',
|
||||
value: 'first-value',
|
||||
meta: getMockPlugin({ name: 'mock-data-name', id: 'mock-data-id' }),
|
||||
sort: '',
|
||||
},
|
||||
{
|
||||
name: 'second-name',
|
||||
value: 'second-value',
|
||||
meta: getMockPlugin({ name: 'mock-data-name', id: 'mock-data-id' }),
|
||||
sort: '',
|
||||
},
|
||||
];
|
||||
|
||||
const getMetricSourcesMock = jest.fn().mockResolvedValue(sources);
|
||||
const getDatasourceSrvMock = jest.fn().mockReturnValue({ getMetricSources: getMetricSourcesMock });
|
||||
const dependencies: DataSourceVariableActionDependencies = { getDatasourceSrv: getDatasourceSrvMock };
|
||||
const datasource = variableMockBuilder('datasource')
|
||||
.withUuid('0')
|
||||
.withQuery('mock-data-id')
|
||||
.withRegEx('/.*(second-name).*/')
|
||||
.create();
|
||||
const tester = await reduxTester<{ templating: TemplatingState }>()
|
||||
.givenRootReducer(getTemplatingRootReducer())
|
||||
.whenActionIsDispatched(initDashboardTemplating([datasource]))
|
||||
.whenAsyncActionIsDispatched(
|
||||
updateDataSourceVariableOptions(toVariableIdentifier(datasource), dependencies),
|
||||
true
|
||||
);
|
||||
|
||||
await tester.thenDispatchedActionShouldEqual(
|
||||
createDataSourceOptions(
|
||||
toVariablePayload({ type: 'datasource', uuid: '0' }, { sources, regex: /.*(second-name).*/ })
|
||||
),
|
||||
setCurrentVariableValue(
|
||||
toVariablePayload(
|
||||
{ type: 'datasource', uuid: '0' },
|
||||
{ option: { text: 'second-name', value: 'second-name', selected: false } }
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
expect(getMetricSourcesMock).toHaveBeenCalledTimes(1);
|
||||
expect(getMetricSourcesMock).toHaveBeenCalledWith({ skipVariables: true });
|
||||
expect(getDatasourceSrvMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when initDataSourceVariableEditor is dispatched', () => {
|
||||
it('then the correct actions are dispatched', async () => {
|
||||
const sources: DataSourceSelectItem[] = [
|
||||
{
|
||||
name: 'first-name',
|
||||
value: 'first-value',
|
||||
meta: getMockPlugin({ name: 'mock-data-name', id: 'mock-data-id' }),
|
||||
sort: '',
|
||||
},
|
||||
{
|
||||
name: 'second-name',
|
||||
value: 'second-value',
|
||||
meta: getMockPlugin({ name: 'mock-data-name', id: 'mock-data-id' }),
|
||||
sort: '',
|
||||
},
|
||||
{
|
||||
name: 'mixed-name',
|
||||
value: 'mixed-value',
|
||||
meta: getMockPlugin(({
|
||||
name: 'mixed-data-name',
|
||||
id: 'mixed-data-id',
|
||||
mixed: true,
|
||||
} as unknown) as DataSourcePluginMeta),
|
||||
sort: '',
|
||||
},
|
||||
];
|
||||
|
||||
const getMetricSourcesMock = jest.fn().mockResolvedValue(sources);
|
||||
const getDatasourceSrvMock = jest.fn().mockReturnValue({ getMetricSources: getMetricSourcesMock });
|
||||
const dependencies: DataSourceVariableActionDependencies = { getDatasourceSrv: getDatasourceSrvMock };
|
||||
|
||||
const tester = await reduxTester<{ templating: TemplatingState }>()
|
||||
.givenRootReducer(getTemplatingRootReducer())
|
||||
.whenAsyncActionIsDispatched(initDataSourceVariableEditor(dependencies));
|
||||
|
||||
await tester.thenDispatchedActionShouldEqual(
|
||||
changeVariableEditorExtended({
|
||||
propName: 'dataSourceTypes',
|
||||
propValue: [
|
||||
{ text: '', value: '' },
|
||||
{ text: 'mock-data-name', value: 'mock-data-id' },
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
expect(getMetricSourcesMock).toHaveBeenCalledTimes(1);
|
||||
expect(getMetricSourcesMock).toHaveBeenCalledWith();
|
||||
expect(getDatasourceSrvMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
54
public/app/features/variables/datasource/actions.ts
Normal file
54
public/app/features/variables/datasource/actions.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { toVariablePayload, VariableIdentifier } from '../state/types';
|
||||
import { ThunkResult } from '../../../types';
|
||||
import { createDataSourceOptions } from './reducer';
|
||||
import { validateVariableSelectionState } from '../state/actions';
|
||||
import { DataSourceSelectItem, stringToJsRegex } from '@grafana/data';
|
||||
import { getDatasourceSrv } from '../../plugins/datasource_srv';
|
||||
import { getVariable } from '../state/selectors';
|
||||
import { DataSourceVariableModel } from '../../templating/variable';
|
||||
import templateSrv from '../../templating/template_srv';
|
||||
import _ from 'lodash';
|
||||
import { changeVariableEditorExtended } from '../editor/reducer';
|
||||
|
||||
export interface DataSourceVariableActionDependencies {
|
||||
getDatasourceSrv: typeof getDatasourceSrv;
|
||||
}
|
||||
|
||||
export const updateDataSourceVariableOptions = (
|
||||
identifier: VariableIdentifier,
|
||||
dependencies: DataSourceVariableActionDependencies = { getDatasourceSrv: getDatasourceSrv }
|
||||
): ThunkResult<void> => async (dispatch, getState) => {
|
||||
const sources = await dependencies.getDatasourceSrv().getMetricSources({ skipVariables: true });
|
||||
const variableInState = getVariable<DataSourceVariableModel>(identifier.uuid!, getState());
|
||||
let regex;
|
||||
|
||||
if (variableInState.regex) {
|
||||
regex = templateSrv.replace(variableInState.regex, undefined, 'regex');
|
||||
regex = stringToJsRegex(regex);
|
||||
}
|
||||
|
||||
await dispatch(createDataSourceOptions(toVariablePayload(identifier, { sources, regex })));
|
||||
await dispatch(validateVariableSelectionState(identifier));
|
||||
};
|
||||
|
||||
export const initDataSourceVariableEditor = (
|
||||
dependencies: DataSourceVariableActionDependencies = { getDatasourceSrv: getDatasourceSrv }
|
||||
): ThunkResult<void> => async dispatch => {
|
||||
const dataSources: DataSourceSelectItem[] = await dependencies.getDatasourceSrv().getMetricSources();
|
||||
const filtered = dataSources.filter(ds => !ds.meta.mixed && ds.value !== null);
|
||||
const dataSourceTypes = _(filtered)
|
||||
.uniqBy('meta.id')
|
||||
.map((ds: any) => {
|
||||
return { text: ds.meta.name, value: ds.meta.id };
|
||||
})
|
||||
.value();
|
||||
|
||||
dataSourceTypes.unshift({ text: '', value: '' });
|
||||
|
||||
dispatch(
|
||||
changeVariableEditorExtended({
|
||||
propName: 'dataSourceTypes',
|
||||
propValue: dataSourceTypes,
|
||||
})
|
||||
);
|
||||
};
|
46
public/app/features/variables/datasource/adapter.ts
Normal file
46
public/app/features/variables/datasource/adapter.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { containsVariable, DataSourceVariableModel } from '../../templating/variable';
|
||||
import { dispatch } from '../../../store/store';
|
||||
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions';
|
||||
import { VariableAdapter } from '../adapters';
|
||||
import { dataSourceVariableReducer, initialDataSourceVariableModelState } from './reducer';
|
||||
import { OptionsPicker } from '../pickers';
|
||||
import { ALL_VARIABLE_TEXT, toVariableIdentifier } from '../state/types';
|
||||
import { DataSourceVariableEditor } from './DataSourceVariableEditor';
|
||||
import { updateDataSourceVariableOptions } from './actions';
|
||||
|
||||
export const createDataSourceVariableAdapter = (): VariableAdapter<DataSourceVariableModel> => {
|
||||
return {
|
||||
description: 'Enabled you to dynamically switch the datasource for multiple panels',
|
||||
label: 'Datasource',
|
||||
initialState: initialDataSourceVariableModelState,
|
||||
reducer: dataSourceVariableReducer,
|
||||
picker: OptionsPicker,
|
||||
editor: DataSourceVariableEditor,
|
||||
dependsOn: (variable, variableToTest) => {
|
||||
if (variable.regex) {
|
||||
return containsVariable(variable.regex, variableToTest.name);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
setValue: async (variable, option, emitChanges = false) => {
|
||||
await dispatch(setOptionAsCurrent(toVariableIdentifier(variable), option, emitChanges));
|
||||
},
|
||||
setValueFromUrl: async (variable, urlValue) => {
|
||||
await dispatch(setOptionFromUrl(toVariableIdentifier(variable), urlValue));
|
||||
},
|
||||
updateOptions: async variable => {
|
||||
await dispatch(updateDataSourceVariableOptions(toVariableIdentifier(variable)));
|
||||
},
|
||||
getSaveModel: variable => {
|
||||
const { index, uuid, initLock, global, ...rest } = cloneDeep(variable);
|
||||
return { ...rest, options: [] };
|
||||
},
|
||||
getValueForUrl: variable => {
|
||||
if (variable.current.text === ALL_VARIABLE_TEXT) {
|
||||
return ALL_VARIABLE_TEXT;
|
||||
}
|
||||
return variable.current.value;
|
||||
},
|
||||
};
|
||||
};
|
64
public/app/features/variables/datasource/reducer.test.ts
Normal file
64
public/app/features/variables/datasource/reducer.test.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import { reducerTester } from '../../../../test/core/redux/reducerTester';
|
||||
import { VariablesState } from '../state/variablesReducer';
|
||||
import { createDataSourceOptions, dataSourceVariableReducer } from './reducer';
|
||||
import { DataSourceVariableModel } from '../../templating/variable';
|
||||
import { getVariableTestContext } from '../state/helpers';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { createDataSourceVariableAdapter } from './adapter';
|
||||
import { DataSourceSelectItem } from '@grafana/data';
|
||||
import { toVariablePayload } from '../state/types';
|
||||
import { getMockPlugins } from '../../plugins/__mocks__/pluginMocks';
|
||||
|
||||
describe('dataSourceVariableReducer', () => {
|
||||
const adapter = createDataSourceVariableAdapter();
|
||||
describe('when createDataSourceOptions is dispatched', () => {
|
||||
const plugins = getMockPlugins(3);
|
||||
const sources: DataSourceSelectItem[] = plugins.map(p => ({
|
||||
name: p.name,
|
||||
value: `${p.name} value`,
|
||||
meta: p,
|
||||
sort: '',
|
||||
}));
|
||||
|
||||
it.each`
|
||||
query | regex | includeAll | expected
|
||||
${sources[1].meta.id} | ${undefined} | ${false} | ${[{ text: 'pretty cool plugin-1', value: 'pretty cool plugin-1', selected: false }]}
|
||||
${'not-found-plugin'} | ${undefined} | ${false} | ${[{ text: 'No data sources found', value: '', selected: false }]}
|
||||
${sources[1].meta.id} | ${/.*(pretty cool plugin-1).*/} | ${false} | ${[{ text: 'pretty cool plugin-1', value: 'pretty cool plugin-1', selected: false }]}
|
||||
${sources[1].meta.id} | ${/.*(pretty cool plugin-2).*/} | ${false} | ${[{ text: 'No data sources found', value: '', selected: false }]}
|
||||
${sources[1].meta.id} | ${undefined} | ${true} | ${[
|
||||
{ text: 'All', value: '$__all', selected: false },
|
||||
{ text: 'pretty cool plugin-1', value: 'pretty cool plugin-1', selected: false },
|
||||
]}
|
||||
${'not-found-plugin'} | ${undefined} | ${true} | ${[
|
||||
{ text: 'All', value: '$__all', selected: false },
|
||||
{ text: 'No data sources found', value: '', selected: false },
|
||||
]}
|
||||
${sources[1].meta.id} | ${/.*(pretty cool plugin-1).*/} | ${true} | ${[
|
||||
{ text: 'All', value: '$__all', selected: false },
|
||||
{ text: 'pretty cool plugin-1', value: 'pretty cool plugin-1', selected: false },
|
||||
]}
|
||||
${sources[1].meta.id} | ${/.*(pretty cool plugin-2).*/} | ${true} | ${[
|
||||
{ text: 'All', value: '$__all', selected: false },
|
||||
{ text: 'No data sources found', value: '', selected: false },
|
||||
]}
|
||||
`(
|
||||
"when called with query: '$query' and regex: '$regex' and includeAll: '$includeAll' then state should be correct",
|
||||
({ query, regex, includeAll, expected }) => {
|
||||
const { initialState } = getVariableTestContext<DataSourceVariableModel>(adapter, { query, includeAll });
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'datasource' }, { sources, regex });
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(dataSourceVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(createDataSourceOptions(payload))
|
||||
.thenStateShouldEqual({
|
||||
...initialState,
|
||||
['0']: ({
|
||||
...initialState['0'],
|
||||
options: expected,
|
||||
} as unknown) as DataSourceVariableModel,
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
70
public/app/features/variables/datasource/reducer.ts
Normal file
70
public/app/features/variables/datasource/reducer.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { DataSourceVariableModel, VariableHide, VariableOption, VariableRefresh } from '../../templating/variable';
|
||||
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, EMPTY_UUID, getInstanceState, VariablePayload } from '../state/types';
|
||||
import { initialVariablesState, VariablesState } from '../state/variablesReducer';
|
||||
import { DataSourceSelectItem } from '@grafana/data';
|
||||
|
||||
export interface DataSourceVariableEditorState {
|
||||
dataSourceTypes: Array<{ text: string; value: string }>;
|
||||
}
|
||||
|
||||
export const initialDataSourceVariableModelState: DataSourceVariableModel = {
|
||||
uuid: EMPTY_UUID,
|
||||
global: false,
|
||||
type: 'datasource',
|
||||
name: '',
|
||||
hide: VariableHide.dontHide,
|
||||
label: '',
|
||||
current: {} as VariableOption,
|
||||
regex: '',
|
||||
options: [],
|
||||
query: '',
|
||||
multi: false,
|
||||
includeAll: false,
|
||||
refresh: VariableRefresh.onDashboardLoad,
|
||||
skipUrlSync: false,
|
||||
index: -1,
|
||||
initLock: null,
|
||||
};
|
||||
|
||||
export const dataSourceVariableSlice = createSlice({
|
||||
name: 'templating/datasource',
|
||||
initialState: initialVariablesState,
|
||||
reducers: {
|
||||
createDataSourceOptions: (
|
||||
state: VariablesState,
|
||||
action: PayloadAction<VariablePayload<{ sources: DataSourceSelectItem[]; regex: RegExp | undefined }>>
|
||||
) => {
|
||||
const { sources, regex } = action.payload.data;
|
||||
const options: VariableOption[] = [];
|
||||
const instanceState = getInstanceState<DataSourceVariableModel>(state, action.payload.uuid);
|
||||
for (let i = 0; i < sources.length; i++) {
|
||||
const source = sources[i];
|
||||
// must match on type
|
||||
if (source.meta.id !== instanceState.query) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (regex && !regex.exec(source.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
options.push({ text: source.name, value: source.name, selected: false });
|
||||
}
|
||||
|
||||
if (options.length === 0) {
|
||||
options.push({ text: 'No data sources found', value: '', selected: false });
|
||||
}
|
||||
|
||||
if (instanceState.includeAll) {
|
||||
options.unshift({ text: ALL_VARIABLE_TEXT, value: ALL_VARIABLE_VALUE, selected: false });
|
||||
}
|
||||
|
||||
instanceState.options = options;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const dataSourceVariableReducer = dataSourceVariableSlice.reducer;
|
||||
|
||||
export const { createDataSourceOptions } = dataSourceVariableSlice.actions;
|
@ -2,7 +2,7 @@ import React, { FunctionComponent, useCallback } from 'react';
|
||||
import { Switch } from '@grafana/ui';
|
||||
import { e2e } from '@grafana/e2e';
|
||||
|
||||
import { VariableWithMultiSupport } from '../variable';
|
||||
import { VariableWithMultiSupport } from '../../templating/variable';
|
||||
import { VariableEditorProps } from './types';
|
||||
|
||||
export interface SelectionOptionsEditorProps<Model extends VariableWithMultiSupport = VariableWithMultiSupport>
|
@ -7,7 +7,7 @@ import { VariableEditorEditor } from './VariableEditorEditor';
|
||||
import { MapDispatchToProps, MapStateToProps } from 'react-redux';
|
||||
import { connectWithStore } from '../../../core/utils/connectWithReduxStore';
|
||||
import { getVariableClones } from '../state/selectors';
|
||||
import { VariableModel } from '../variable';
|
||||
import { VariableModel } from '../../templating/variable';
|
||||
import { switchToEditMode, switchToListMode, switchToNewMode } from './actions';
|
||||
import { changeVariableOrder, duplicateVariable, removeVariable } from '../state/sharedReducer';
|
||||
|
||||
@ -53,7 +53,7 @@ class VariableEditorContainerUnconnected extends PureComponent<Props> {
|
||||
};
|
||||
|
||||
onDuplicateVariable = (identifier: VariableIdentifier) => {
|
||||
this.props.duplicateVariable(toVariablePayload(identifier));
|
||||
this.props.duplicateVariable(toVariablePayload(identifier, { newUuid: (undefined as unknown) as string }));
|
||||
};
|
||||
|
||||
onRemoveVariable = (identifier: VariableIdentifier) => {
|
@ -5,7 +5,7 @@ import { FormLabel } from '@grafana/ui';
|
||||
import { e2e } from '@grafana/e2e';
|
||||
import { variableAdapters } from '../adapters';
|
||||
import { EMPTY_UUID, toVariablePayload, VariableIdentifier } from '../state/types';
|
||||
import { VariableHide, VariableModel, VariableType } from '../variable';
|
||||
import { VariableHide, VariableModel, VariableType } from '../../templating/variable';
|
||||
import { appEvents } from '../../../core/core';
|
||||
import { VariableValuesPreview } from './VariableValuesPreview';
|
||||
import { changeVariableName, onEditorAdd, onEditorUpdate, variableEditorMount, variableEditorUnMount } from './actions';
|
@ -1,7 +1,7 @@
|
||||
import React, { MouseEvent, PureComponent } from 'react';
|
||||
import { e2e } from '@grafana/e2e';
|
||||
import EmptyListCTA from '../../../core/components/EmptyListCTA/EmptyListCTA';
|
||||
import { QueryVariableModel, VariableModel } from '../variable';
|
||||
import { QueryVariableModel, VariableModel } from '../../templating/variable';
|
||||
import { toVariableIdentifier, VariableIdentifier } from '../state/types';
|
||||
|
||||
export interface Props {
|
@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { VariableModel, VariableOption, VariableWithOptions } from '../variable';
|
||||
import { VariableModel, VariableOption, VariableWithOptions } from '../../templating/variable';
|
||||
import { e2e } from '@grafana/e2e';
|
||||
|
||||
export interface VariableValuesPreviewProps {
|
@ -12,7 +12,7 @@ import { variableAdapters } from '../adapters';
|
||||
import { v4 } from 'uuid';
|
||||
import { AddVariable, EMPTY_UUID, toVariablePayload, VariableIdentifier } from '../state/types';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { VariableType } from '../variable';
|
||||
import { VariableType } from '../../templating/variable';
|
||||
import { addVariable, removeVariable, storeNewVariable } from '../state/sharedReducer';
|
||||
|
||||
export const variableEditorMount = (identifier: VariableIdentifier): ThunkResult<void> => {
|
@ -1,4 +1,4 @@
|
||||
import { VariableModel } from '../variable';
|
||||
import { VariableModel } from '../../templating/variable';
|
||||
|
||||
export interface OnPropChangeArguments<Model extends VariableModel = VariableModel> {
|
||||
propName: keyof Model;
|
@ -1,4 +1,4 @@
|
||||
import { VariableModel, QueryVariableModel } from './variable';
|
||||
import { QueryVariableModel, VariableModel } from '../templating/variable';
|
||||
|
||||
export const isQuery = (model: VariableModel): model is QueryVariableModel => {
|
||||
return model.type === 'query';
|
@ -6,7 +6,12 @@ import { VariableLink } from '../shared/VariableLink';
|
||||
import { VariableInput } from '../shared/VariableInput';
|
||||
import { commitChangesToVariable, filterOrSearchOptions, navigateOptions, toggleAndFetchTag } from './actions';
|
||||
import { OptionsPickerState, showOptions, toggleAllOptions, toggleOption } from './reducer';
|
||||
import { VariableOption, VariableTag, VariableWithMultiSupport, VariableWithOptions } from '../../variable';
|
||||
import {
|
||||
VariableOption,
|
||||
VariableTag,
|
||||
VariableWithMultiSupport,
|
||||
VariableWithOptions,
|
||||
} from '../../../templating/variable';
|
||||
import { VariableOptions } from '../shared/VariableOptions';
|
||||
import { isQuery } from '../../guard';
|
||||
import { VariablePickerProps } from '../types';
|
@ -2,7 +2,7 @@ import { reduxTester } from '../../../../../test/core/redux/reduxTester';
|
||||
import { getTemplatingRootReducer } from '../../state/helpers';
|
||||
import { initDashboardTemplating } from '../../state/actions';
|
||||
import { TemplatingState } from '../../state/reducers';
|
||||
import { QueryVariableModel, VariableHide, VariableRefresh, VariableSort } from '../../variable';
|
||||
import { QueryVariableModel, VariableHide, VariableRefresh, VariableSort } from '../../../templating/variable';
|
||||
import {
|
||||
hideOptions,
|
||||
showOptions,
|
||||
@ -374,8 +374,7 @@ describe('options picker actions', () => {
|
||||
const variable = createVariable({ options, includeAll: false, tags: [tag] });
|
||||
|
||||
datasource.metricFindQuery.mockReset();
|
||||
// @ts-ignore couldn't wrap my head around this
|
||||
// error TS2345: Argument of type '() => Promise<{ value: string; text: string; }[]>' is not assignable to parameter of type '() => Promise<never[]>'.
|
||||
// @ts-ignore strict null error TS2345: Argument of type '() => Promise<{ value: string; text: string; }[]>' is not assignable to parameter of type '() => Promise<never[]>'
|
||||
datasource.metricFindQuery.mockImplementation(() => Promise.resolve(values));
|
||||
|
||||
const tester = await reduxTester<{ templating: TemplatingState }>()
|
@ -8,7 +8,7 @@ import {
|
||||
VariableTag,
|
||||
VariableWithMultiSupport,
|
||||
VariableWithOptions,
|
||||
} from '../../variable';
|
||||
} from '../../../templating/variable';
|
||||
import { variableAdapters } from '../../adapters';
|
||||
import { getVariable } from '../../state/selectors';
|
||||
import { NavigationKey } from '../types';
|
||||
@ -24,7 +24,7 @@ import {
|
||||
} from './reducer';
|
||||
import { getDataSourceSrv } from '@grafana/runtime';
|
||||
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
import { setCurrentVariableValue, changeVariableProp } from '../../state/sharedReducer';
|
||||
import { changeVariableProp, setCurrentVariableValue } from '../../state/sharedReducer';
|
||||
import { toVariablePayload } from '../../state/types';
|
||||
|
||||
export const navigateOptions = (key: NavigationKey, clearOthers: boolean): ThunkResult<void> => {
|
@ -14,7 +14,7 @@ import {
|
||||
updateSearchQuery,
|
||||
} from './reducer';
|
||||
import { reducerTester } from '../../../../../test/core/redux/reducerTester';
|
||||
import { QueryVariableModel, VariableTag } from '../../variable';
|
||||
import { QueryVariableModel, VariableTag } from '../../../templating/variable';
|
||||
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from '../../state/types';
|
||||
|
||||
const getVariableTestContext = (extend: Partial<OptionsPickerState>) => {
|
@ -1,6 +1,11 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { containsSearchFilter, VariableOption, VariableTag, VariableWithMultiSupport } from '../../variable';
|
||||
import {
|
||||
containsSearchFilter,
|
||||
VariableOption,
|
||||
VariableTag,
|
||||
VariableWithMultiSupport,
|
||||
} from '../../../templating/variable';
|
||||
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from '../../state/types';
|
||||
import { isQuery } from '../../guard';
|
||||
import { applyStateChanges } from '../../../../core/utils/applyStateChanges';
|
@ -1,5 +1,5 @@
|
||||
import React, { FunctionComponent, useMemo } from 'react';
|
||||
import { VariableHide, VariableModel } from '../variable';
|
||||
import { VariableHide, VariableModel } from '../../templating/variable';
|
||||
import { e2e } from '@grafana/e2e';
|
||||
import { variableAdapters } from '../adapters';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { getTagColorsFromName } from '@grafana/ui';
|
||||
import { e2e } from '@grafana/e2e';
|
||||
import { VariableTag } from '../../variable';
|
||||
import { VariableTag } from '../../../templating/variable';
|
||||
|
||||
interface Props {
|
||||
onClick: () => void;
|
@ -1,7 +1,7 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { getTagColorsFromName, Tooltip } from '@grafana/ui';
|
||||
import { e2e } from '@grafana/e2e';
|
||||
import { VariableOption, VariableTag } from '../../variable';
|
||||
import { VariableOption, VariableTag } from '../../../templating/variable';
|
||||
|
||||
export interface Props {
|
||||
multi: boolean;
|
@ -1,4 +1,4 @@
|
||||
import { VariableModel } from '../variable';
|
||||
import { VariableModel } from '../../templating/variable';
|
||||
|
||||
export interface VariablePickerProps<Model extends VariableModel = VariableModel> {
|
||||
variable: Model;
|
@ -2,9 +2,9 @@ import React, { ChangeEvent, PureComponent } from 'react';
|
||||
import { e2e } from '@grafana/e2e';
|
||||
import { FormLabel, Switch } from '@grafana/ui';
|
||||
|
||||
import templateSrv from '../template_srv';
|
||||
import templateSrv from '../../templating/template_srv';
|
||||
import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor';
|
||||
import { QueryVariableModel, VariableRefresh, VariableSort, VariableWithMultiSupport } from '../variable';
|
||||
import { QueryVariableModel, VariableRefresh, VariableSort, VariableWithMultiSupport } from '../../templating/variable';
|
||||
import { QueryVariableEditorState } from './reducer';
|
||||
import { changeQueryVariableDataSource, changeQueryVariableQuery, initQueryVariableEditor } from './actions';
|
||||
import { VariableEditorState } from '../editor/reducer';
|
@ -2,7 +2,7 @@ import { variableAdapters } from '../adapters';
|
||||
import { createQueryVariableAdapter } from './adapter';
|
||||
import { reduxTester } from '../../../../test/core/redux/reduxTester';
|
||||
import { getTemplatingRootReducer } from '../state/helpers';
|
||||
import { QueryVariableModel, VariableHide, VariableRefresh, VariableSort } from '../variable';
|
||||
import { QueryVariableModel, VariableHide, VariableRefresh, VariableSort } from '../../templating/variable';
|
||||
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, toVariablePayload } from '../state/types';
|
||||
import { changeVariableProp, setCurrentVariableValue } from '../state/sharedReducer';
|
||||
import { initDashboardTemplating } from '../state/actions';
|
||||
@ -20,7 +20,7 @@ import {
|
||||
removeVariableEditorError,
|
||||
setIdInEditor,
|
||||
} from '../editor/reducer';
|
||||
import DefaultVariableQueryEditor from '../DefaultVariableQueryEditor';
|
||||
import DefaultVariableQueryEditor from '../../templating/DefaultVariableQueryEditor';
|
||||
import { expect } from 'test/lib/common';
|
||||
|
||||
const mocks: Record<string, any> = {
|
@ -1,13 +1,13 @@
|
||||
import { AppEvents, DataSourcePluginMeta, DataSourceSelectItem } from '@grafana/data';
|
||||
|
||||
import { validateVariableSelectionState } from '../state/actions';
|
||||
import { QueryVariableModel, VariableRefresh } from '../variable';
|
||||
import { QueryVariableModel, VariableRefresh } from '../../templating/variable';
|
||||
import { ThunkResult } from '../../../types';
|
||||
import { getDatasourceSrv } from '../../plugins/datasource_srv';
|
||||
import { getTimeSrv } from '../../dashboard/services/TimeSrv';
|
||||
import appEvents from '../../../core/app_events';
|
||||
import { importDataSourcePlugin } from '../../plugins/plugin_loader';
|
||||
import DefaultVariableQueryEditor from '../DefaultVariableQueryEditor';
|
||||
import DefaultVariableQueryEditor from '../../templating/DefaultVariableQueryEditor';
|
||||
import { getVariable } from '../state/selectors';
|
||||
import { addVariableEditorError, changeVariableEditorExtended, removeVariableEditorError } from '../editor/reducer';
|
||||
import { variableAdapters } from '../adapters';
|
@ -1,6 +1,6 @@
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
|
||||
import { containsVariable, QueryVariableModel, VariableRefresh } from '../variable';
|
||||
import { containsVariable, QueryVariableModel, VariableRefresh } from '../../templating/variable';
|
||||
import { initialQueryVariableModelState, queryVariableReducer } from './reducer';
|
||||
import { dispatch } from '../../../store/store';
|
||||
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions';
|
@ -1,6 +1,6 @@
|
||||
import { reducerTester } from '../../../../test/core/redux/reducerTester';
|
||||
import { queryVariableReducer, updateVariableOptions, updateVariableTags } from './reducer';
|
||||
import { QueryVariableModel, VariableOption } from '../variable';
|
||||
import { QueryVariableModel, VariableOption } from '../../templating/variable';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { VariablesState } from '../state/variablesReducer';
|
||||
import { getVariableTestContext } from '../state/helpers';
|
@ -9,8 +9,8 @@ import {
|
||||
VariableRefresh,
|
||||
VariableSort,
|
||||
VariableTag,
|
||||
} from '../variable';
|
||||
import templateSrv from '../template_srv';
|
||||
} from '../../templating/variable';
|
||||
import templateSrv from '../../templating/template_srv';
|
||||
import {
|
||||
ALL_VARIABLE_TEXT,
|
||||
ALL_VARIABLE_VALUE,
|
@ -7,7 +7,7 @@ import { createCustomVariableAdapter } from '../custom/adapter';
|
||||
import { createTextBoxVariableAdapter } from '../textbox/adapter';
|
||||
import { createConstantVariableAdapter } from '../constant/adapter';
|
||||
import { reduxTester } from '../../../../test/core/redux/reduxTester';
|
||||
import { TemplatingState } from 'app/features/templating/state/reducers';
|
||||
import { TemplatingState } from 'app/features/variables/state/reducers';
|
||||
import { initDashboardTemplating, processVariables, setOptionFromUrl, validateVariableSelectionState } from './actions';
|
||||
import { addInitLock, addVariable, removeInitLock, resolveInitLock, setCurrentVariableValue } from './sharedReducer';
|
||||
import { toVariableIdentifier, toVariablePayload } from './types';
|
@ -1,7 +1,13 @@
|
||||
import castArray from 'lodash/castArray';
|
||||
import { UrlQueryMap, UrlQueryValue } from '@grafana/runtime';
|
||||
|
||||
import { QueryVariableModel, VariableModel, VariableOption, VariableRefresh, VariableWithOptions } from '../variable';
|
||||
import {
|
||||
QueryVariableModel,
|
||||
VariableModel,
|
||||
VariableOption,
|
||||
VariableRefresh,
|
||||
VariableWithOptions,
|
||||
} from '../../templating/variable';
|
||||
import { StoreState, ThunkResult } from '../../../types';
|
||||
import { getVariable, getVariables } from './selectors';
|
||||
import { variableAdapters } from '../adapters';
|
@ -2,7 +2,7 @@ import { combineReducers } from '@reduxjs/toolkit';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
|
||||
import { EMPTY_UUID } from './types';
|
||||
import { VariableHide, VariableModel, VariableRefresh, VariableType } from '../variable';
|
||||
import { VariableHide, VariableModel, VariableRefresh, VariableType } from '../../templating/variable';
|
||||
import { variablesReducer, VariablesState } from './variablesReducer';
|
||||
import { optionsPickerReducer } from '../pickers/OptionsPicker/reducer';
|
||||
import { variableEditorReducer } from '../editor/reducer';
|
||||
@ -106,6 +106,11 @@ export const variableMockBuilder = (type: VariableType) => {
|
||||
return instance;
|
||||
};
|
||||
|
||||
const withRegEx = (regex: any) => {
|
||||
model.regex = regex;
|
||||
return instance;
|
||||
};
|
||||
|
||||
const create = () => model;
|
||||
|
||||
const instance = {
|
||||
@ -116,6 +121,7 @@ export const variableMockBuilder = (type: VariableType) => {
|
||||
withRefresh,
|
||||
withQuery,
|
||||
withMulti,
|
||||
withRegEx,
|
||||
create,
|
||||
};
|
||||
|
@ -5,11 +5,11 @@ import { variableAdapters } from '../adapters';
|
||||
import { createQueryVariableAdapter } from '../query/adapter';
|
||||
import { createCustomVariableAdapter } from '../custom/adapter';
|
||||
import { reduxTester } from '../../../../test/core/redux/reduxTester';
|
||||
import { TemplatingState } from 'app/features/templating/state/reducers';
|
||||
import { TemplatingState } from 'app/features/variables/state/reducers';
|
||||
import { initDashboardTemplating, processVariable } from './actions';
|
||||
import { resolveInitLock, setCurrentVariableValue } from './sharedReducer';
|
||||
import { toVariableIdentifier, toVariablePayload } from './types';
|
||||
import { VariableRefresh } from '../variable';
|
||||
import { VariableRefresh } from '../../templating/variable';
|
||||
import { updateVariableOptions } from '../query/reducer';
|
||||
|
||||
jest.mock('app/features/dashboard/services/TimeSrv', () => ({
|
@ -1,6 +1,6 @@
|
||||
import { reducerTester } from '../../../../test/core/redux/reducerTester';
|
||||
import { cleanUpDashboard } from 'app/features/dashboard/state/reducers';
|
||||
import { VariableHide, VariableModel } from '../variable';
|
||||
import { VariableHide, VariableModel } from '../../templating/variable';
|
||||
import { VariableAdapter, variableAdapters } from '../adapters';
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
import { variablesReducer, VariablesState } from './variablesReducer';
|
@ -2,7 +2,7 @@ import { combineReducers } from '@reduxjs/toolkit';
|
||||
import { optionsPickerReducer, OptionsPickerState } from '../pickers/OptionsPicker/reducer';
|
||||
import { variableEditorReducer, VariableEditorState } from '../editor/reducer';
|
||||
import { variablesReducer } from './variablesReducer';
|
||||
import { VariableModel } from '../variable';
|
||||
import { VariableModel } from '../../templating/variable';
|
||||
|
||||
export interface TemplatingState {
|
||||
variables: Record<string, VariableModel>;
|
@ -1,7 +1,7 @@
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
import { StoreState } from '../../../types';
|
||||
import { VariableModel } from '../variable';
|
||||
import { VariableModel } from '../../templating/variable';
|
||||
import { getState } from '../../../store/store';
|
||||
import { EMPTY_UUID } from './types';
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
sharedReducer,
|
||||
storeNewVariable,
|
||||
} from './sharedReducer';
|
||||
import { QueryVariableModel, VariableHide } from '../variable';
|
||||
import { QueryVariableModel, VariableHide } from '../../templating/variable';
|
||||
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, EMPTY_UUID, toVariablePayload } from './types';
|
||||
import { variableAdapters } from '../adapters';
|
||||
import { createQueryVariableAdapter } from '../query/adapter';
|
@ -1,7 +1,7 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
|
||||
import { VariableModel, VariableOption, VariableType, VariableWithOptions } from '../variable';
|
||||
import { VariableModel, VariableOption, VariableType, VariableWithOptions } from '../../templating/variable';
|
||||
import { AddVariable, ALL_VARIABLE_VALUE, EMPTY_UUID, getInstanceState, VariablePayload } from './types';
|
||||
import { variableAdapters } from '../adapters';
|
||||
import { changeVariableNameSucceeded } from '../editor/reducer';
|
||||
@ -100,8 +100,12 @@ const sharedReducerSlice = createSlice({
|
||||
},
|
||||
setCurrentVariableValue: (
|
||||
state: VariablesState,
|
||||
action: PayloadAction<VariablePayload<{ option: VariableOption }>>
|
||||
action: PayloadAction<VariablePayload<{ option: VariableOption | undefined }>>
|
||||
) => {
|
||||
if (!action.payload.data.option) {
|
||||
return;
|
||||
}
|
||||
|
||||
const instanceState = getInstanceState<VariableWithOptions>(state, action.payload.uuid);
|
||||
const current = { ...action.payload.data.option };
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { VariableModel, VariableType } from '../variable';
|
||||
import { VariableModel, VariableType } from '../../templating/variable';
|
||||
import { VariablesState } from './variablesReducer';
|
||||
|
||||
export const EMPTY_UUID = '00000000-0000-0000-0000-000000000000';
|
@ -2,7 +2,7 @@ import { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { cleanUpDashboard } from '../../dashboard/state/reducers';
|
||||
import { variableAdapters } from '../adapters';
|
||||
import { sharedReducer } from './sharedReducer';
|
||||
import { VariableModel } from '../variable';
|
||||
import { VariableModel } from '../../templating/variable';
|
||||
import { VariablePayload } from './types';
|
||||
|
||||
export interface VariablesState extends Record<string, VariableModel> {}
|
@ -1,5 +1,5 @@
|
||||
import React, { ChangeEvent, PureComponent } from 'react';
|
||||
import { TextBoxVariableModel } from '../variable';
|
||||
import { TextBoxVariableModel } from '../../templating/variable';
|
||||
import { VariableEditorProps } from '../editor/types';
|
||||
|
||||
export interface Props extends VariableEditorProps<TextBoxVariableModel> {}
|
@ -1,6 +1,6 @@
|
||||
import React, { ChangeEvent, FocusEvent, KeyboardEvent, PureComponent } from 'react';
|
||||
|
||||
import { TextBoxVariableModel } from '../variable';
|
||||
import { TextBoxVariableModel } from '../../templating/variable';
|
||||
import { toVariablePayload } from '../state/types';
|
||||
import { dispatch } from '../../../store/store';
|
||||
import { variableAdapters } from '../adapters';
|
@ -1,10 +1,10 @@
|
||||
import { variableAdapters } from '../adapters';
|
||||
import { createTextBoxVariableAdapter } from './adapter';
|
||||
import { reduxTester } from '../../../../test/core/redux/reduxTester';
|
||||
import { TemplatingState } from 'app/features/templating/state/reducers';
|
||||
import { TemplatingState } from 'app/features/variables/state/reducers';
|
||||
import { updateTextBoxVariableOptions } from './actions';
|
||||
import { getTemplatingRootReducer } from '../state/helpers';
|
||||
import { VariableOption, VariableHide, TextBoxVariableModel } from '../variable';
|
||||
import { TextBoxVariableModel, VariableHide, VariableOption } from '../../templating/variable';
|
||||
import { toVariablePayload } from '../state/types';
|
||||
import { createTextBoxOptions } from './reducer';
|
||||
import { setCurrentVariableValue } from '../state/sharedReducer';
|
@ -1,4 +1,4 @@
|
||||
import { TextBoxVariableModel } from '../variable';
|
||||
import { TextBoxVariableModel } from '../../templating/variable';
|
||||
import { ThunkResult } from '../../../types';
|
||||
import { getVariable } from '../state/selectors';
|
||||
import { variableAdapters } from '../adapters';
|
@ -1,6 +1,6 @@
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
|
||||
import { TextBoxVariableModel } from '../variable';
|
||||
import { TextBoxVariableModel } from '../../templating/variable';
|
||||
import { initialTextBoxVariableModelState, textBoxVariableReducer } from './reducer';
|
||||
import { dispatch } from '../../../store/store';
|
||||
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions';
|
@ -2,9 +2,9 @@ import { reducerTester } from '../../../../test/core/redux/reducerTester';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { getVariableTestContext } from '../state/helpers';
|
||||
import { toVariablePayload } from '../state/types';
|
||||
import { textBoxVariableReducer, createTextBoxOptions } from './reducer';
|
||||
import { createTextBoxOptions, textBoxVariableReducer } from './reducer';
|
||||
import { VariablesState } from '../state/variablesReducer';
|
||||
import { TextBoxVariableModel } from '../variable';
|
||||
import { TextBoxVariableModel } from '../../templating/variable';
|
||||
import { createTextBoxVariableAdapter } from './adapter';
|
||||
|
||||
describe('textBoxVariableReducer', () => {
|
@ -1,6 +1,6 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
import { TextBoxVariableModel, VariableHide, VariableOption } from '../variable';
|
||||
import { TextBoxVariableModel, VariableHide, VariableOption } from '../../templating/variable';
|
||||
import { EMPTY_UUID, getInstanceState, VariablePayload } from '../state/types';
|
||||
import { initialVariablesState, VariablesState } from '../state/variablesReducer';
|
||||
|
@ -18,7 +18,7 @@ import { LdapState } from './ldap';
|
||||
import { PanelEditorState } from '../features/dashboard/panel_editor/state/reducers';
|
||||
import { PanelEditorStateNew } from '../features/dashboard/components/PanelEditor/state/reducers';
|
||||
import { ApiKeysState } from './apiKeys';
|
||||
import { TemplatingState } from '../features/templating/state/reducers';
|
||||
import { TemplatingState } from '../features/variables/state/reducers';
|
||||
|
||||
export interface StoreState {
|
||||
navIndex: NavIndex;
|
||||
|
Loading…
Reference in New Issue
Block a user