mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Variables: replaces UUID with name for a more performant lookup in TemplateSrv (#22858)
* Refactor: renames uuid to id * Refactor: misc renames * Refactor: fixes renaming of variable * Refactor: changes method accessed by templateSrv * Refactor: fixes for NEW_VARIABLE_ID * Refactor: rename flow refactor * Tests: adds missing reducer and action tests * Refactor: keeping tests consitent * Chore: reorder imports * Chore: removes uuid package * Refactor: fixes imports
This commit is contained in:
@@ -211,7 +211,6 @@
|
||||
"@types/md5": "^2.1.33",
|
||||
"@types/react-loadable": "5.5.2",
|
||||
"@types/react-virtualized-auto-sizer": "1.0.0",
|
||||
"@types/uuid": "3.4.7",
|
||||
"abortcontroller-polyfill": "1.4.0",
|
||||
"angular": "1.6.9",
|
||||
"angular-bindonce": "0.3.1",
|
||||
@@ -275,7 +274,6 @@
|
||||
"tether-drop": "https://github.com/torkelo/drop/tarball/master",
|
||||
"tinycolor2": "1.4.1",
|
||||
"tti-polyfill": "0.2.2",
|
||||
"uuid": "3.4.0",
|
||||
"whatwg-fetch": "3.0.0",
|
||||
"xss": "1.0.6"
|
||||
},
|
||||
|
@@ -22,7 +22,7 @@ export const SubMenuItems: FunctionComponent<Props> = ({ variables }) => {
|
||||
{visibleVariables.map(variable => {
|
||||
return (
|
||||
<div
|
||||
key={variable.uuid}
|
||||
key={variable.id}
|
||||
className="submenu-item gf-form-inline"
|
||||
aria-label={e2e.pages.Dashboard.SubMenu.selectors.submenuItem}
|
||||
>
|
||||
|
@@ -4,7 +4,6 @@ import { variableRegex } from 'app/features/templating/variable';
|
||||
import { escapeHtml } from 'app/core/utils/text';
|
||||
import { ScopedVars, TimeRange } from '@grafana/data';
|
||||
import { getVariableWithName, getFilteredVariables } from '../variables/state/selectors';
|
||||
import { getState } from '../../store/store';
|
||||
import { getConfig } from 'app/core/config';
|
||||
import { isAdHoc } from '../variables/guard';
|
||||
|
||||
@@ -378,7 +377,7 @@ export class TemplateSrv {
|
||||
}
|
||||
|
||||
if (getConfig().featureToggles.newVariables && !this.index[name]) {
|
||||
return getVariableWithName(name, getState());
|
||||
return getVariableWithName(name);
|
||||
}
|
||||
|
||||
return this.index[name];
|
||||
|
@@ -139,7 +139,7 @@ export interface VariableWithOptions extends VariableModel {
|
||||
}
|
||||
|
||||
export interface VariableModel {
|
||||
uuid?: string; // only exists for variables in redux state
|
||||
id?: string; // only exists for variables in redux state
|
||||
global?: boolean; // only exists for variables in redux state
|
||||
type: VariableType;
|
||||
name: string;
|
||||
|
@@ -1,36 +1,32 @@
|
||||
import { DataSourcePluginMeta, DataSourceSelectItem } from '@grafana/data';
|
||||
|
||||
import { variableAdapters } from '../adapters';
|
||||
import { createAdHocVariableAdapter } from './adapter';
|
||||
import { reduxTester } from '../../../../test/core/redux/reduxTester';
|
||||
import { TemplatingState } from 'app/features/variables/state/reducers';
|
||||
import { getRootReducer } from '../state/helpers';
|
||||
import { toVariablePayload, toVariableIdentifier } from '../state/types';
|
||||
import * as variableBuilder from '../shared/testing/builders';
|
||||
import { toVariableIdentifier, toVariablePayload } from '../state/types';
|
||||
import {
|
||||
applyFilterFromTable,
|
||||
AdHocTableOptions,
|
||||
changeFilter,
|
||||
addFilter,
|
||||
AdHocTableOptions,
|
||||
applyFilterFromTable,
|
||||
changeFilter,
|
||||
changeVariableDatasource,
|
||||
initAdHocVariableEditor,
|
||||
removeFilter,
|
||||
setFiltersFromUrl,
|
||||
initAdHocVariableEditor,
|
||||
changeVariableDatasource,
|
||||
} from './actions';
|
||||
import { filterAdded, filterUpdated, filterRemoved, filtersRestored } from './reducer';
|
||||
import { filterAdded, filterRemoved, filtersRestored, filterUpdated } from './reducer';
|
||||
import { addVariable, changeVariableProp } from '../state/sharedReducer';
|
||||
import { updateLocation } from 'app/core/actions';
|
||||
import { DashboardState, LocationState } from 'app/types';
|
||||
import { VariableModel } from 'app/features/templating/variable';
|
||||
import { changeVariableEditorExtended, setIdInEditor } from '../editor/reducer';
|
||||
import { DataSourceSelectItem, DataSourcePluginMeta } from '@grafana/data';
|
||||
import { adHocBuilder } from '../shared/testing/builders';
|
||||
|
||||
const uuid = '0';
|
||||
const getMetricSources = jest.fn().mockReturnValue([]);
|
||||
const getDatasource = jest.fn().mockResolvedValue({});
|
||||
|
||||
jest.mock('uuid', () => ({
|
||||
v4: jest.fn(() => uuid),
|
||||
}));
|
||||
|
||||
jest.mock('app/features/plugins/datasource_srv', () => ({
|
||||
getDatasourceSrv: jest.fn(() => ({
|
||||
get: getDatasource,
|
||||
@@ -63,11 +59,10 @@ describe('adhoc actions', () => {
|
||||
condition: '',
|
||||
};
|
||||
|
||||
const variable = variableBuilder
|
||||
.adHoc()
|
||||
const variable = adHocBuilder()
|
||||
.withId('Filters')
|
||||
.withName('Filters')
|
||||
.withFilters([existingFilter])
|
||||
.withUUID(uuid)
|
||||
.withDatasource(options.datasource)
|
||||
.build();
|
||||
|
||||
@@ -79,15 +74,10 @@ describe('adhoc actions', () => {
|
||||
const expectedQuery = { 'var-Filters': ['filter-key|!=|filter-existing', 'filter-key|=|filter-value'] };
|
||||
const expectedFilter = { key: 'filter-key', value: 'filter-value', operator: '=', condition: '' };
|
||||
|
||||
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
|
||||
const [addFilterAction, updateLocationAction] = actions;
|
||||
const expectedNumberOfActions = 2;
|
||||
|
||||
expect(addFilterAction).toEqual(filterAdded(toVariablePayload(variable, expectedFilter)));
|
||||
expect(updateLocationAction).toEqual(updateLocation({ query: expectedQuery }));
|
||||
|
||||
return actions.length === expectedNumberOfActions;
|
||||
});
|
||||
tester.thenDispatchedActionsShouldEqual(
|
||||
filterAdded(toVariablePayload(variable, expectedFilter)),
|
||||
updateLocation({ query: expectedQuery })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -104,26 +94,20 @@ describe('adhoc actions', () => {
|
||||
.givenRootReducer(getRootReducer())
|
||||
.whenAsyncActionIsDispatched(applyFilterFromTable(options), true);
|
||||
|
||||
const variable = variableBuilder
|
||||
.adHoc()
|
||||
const variable = adHocBuilder()
|
||||
.withId('Filters')
|
||||
.withName('Filters')
|
||||
.withUUID(uuid)
|
||||
.withDatasource(options.datasource)
|
||||
.build();
|
||||
|
||||
const expectedQuery = { 'var-Filters': ['filter-key|=|filter-value'] };
|
||||
const expectedFilter = { key: 'filter-key', value: 'filter-value', operator: '=', condition: '' };
|
||||
|
||||
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
|
||||
const [addVariableAction, addFilterAction, updateLocationAction] = actions;
|
||||
const expectedNumberOfActions = 3;
|
||||
|
||||
expect(addVariableAction).toEqual(createAddVariableAction(variable));
|
||||
expect(addFilterAction).toEqual(filterAdded(toVariablePayload(variable, expectedFilter)));
|
||||
expect(updateLocationAction).toEqual(updateLocation({ query: expectedQuery }));
|
||||
|
||||
return actions.length === expectedNumberOfActions;
|
||||
});
|
||||
tester.thenDispatchedActionsShouldEqual(
|
||||
createAddVariableAction(variable),
|
||||
filterAdded(toVariablePayload(variable, expectedFilter)),
|
||||
updateLocation({ query: expectedQuery })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -136,10 +120,9 @@ describe('adhoc actions', () => {
|
||||
operator: '=',
|
||||
};
|
||||
|
||||
const variable = variableBuilder
|
||||
.adHoc()
|
||||
const variable = adHocBuilder()
|
||||
.withId('Filters')
|
||||
.withName('Filters')
|
||||
.withUUID(uuid)
|
||||
.withFilters([])
|
||||
.withDatasource(options.datasource)
|
||||
.build();
|
||||
@@ -152,15 +135,10 @@ describe('adhoc actions', () => {
|
||||
const expectedFilter = { key: 'filter-key', value: 'filter-value', operator: '=', condition: '' };
|
||||
const expectedQuery = { 'var-Filters': ['filter-key|=|filter-value'] };
|
||||
|
||||
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
|
||||
const [addFilterAction, updateLocationAction] = actions;
|
||||
const expectedNumberOfActions = 2;
|
||||
|
||||
expect(addFilterAction).toEqual(filterAdded(toVariablePayload(variable, expectedFilter)));
|
||||
expect(updateLocationAction).toEqual(updateLocation({ query: expectedQuery }));
|
||||
|
||||
return actions.length === expectedNumberOfActions;
|
||||
});
|
||||
tester.thenDispatchedActionsShouldEqual(
|
||||
filterAdded(toVariablePayload(variable, expectedFilter)),
|
||||
updateLocation({ query: expectedQuery })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -173,16 +151,15 @@ describe('adhoc actions', () => {
|
||||
operator: '=',
|
||||
};
|
||||
|
||||
const existing = variableBuilder
|
||||
.adHoc()
|
||||
const existing = adHocBuilder()
|
||||
.withId('elastic-filter')
|
||||
.withName('elastic-filter')
|
||||
.withDatasource('elasticsearch')
|
||||
.build();
|
||||
|
||||
const variable = variableBuilder
|
||||
.adHoc()
|
||||
const variable = adHocBuilder()
|
||||
.withId('Filters')
|
||||
.withName('Filters')
|
||||
.withUUID(uuid)
|
||||
.withDatasource(options.datasource)
|
||||
.build();
|
||||
|
||||
@@ -192,18 +169,13 @@ describe('adhoc actions', () => {
|
||||
.whenAsyncActionIsDispatched(applyFilterFromTable(options), true);
|
||||
|
||||
const expectedFilter = { key: 'filter-key', value: 'filter-value', operator: '=', condition: '' };
|
||||
const expectedQuery = { 'var-Filters': ['filter-key|=|filter-value'] };
|
||||
const expectedQuery = { 'var-elastic-filter': [] as string[], 'var-Filters': ['filter-key|=|filter-value'] };
|
||||
|
||||
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
|
||||
const [addVariableAction, addFilterAction, updateLocationAction] = actions;
|
||||
const expectedNumberOfActions = 3;
|
||||
|
||||
expect(addVariableAction).toEqual(createAddVariableAction(variable, 1));
|
||||
expect(addFilterAction).toEqual(filterAdded(toVariablePayload(variable, expectedFilter)));
|
||||
expect(updateLocationAction).toEqual(updateLocation({ query: expectedQuery }));
|
||||
|
||||
return actions.length === expectedNumberOfActions;
|
||||
});
|
||||
tester.thenDispatchedActionsShouldEqual(
|
||||
createAddVariableAction(variable, 1),
|
||||
filterAdded(toVariablePayload(variable, expectedFilter)),
|
||||
updateLocation({ query: expectedQuery })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -221,11 +193,10 @@ describe('adhoc actions', () => {
|
||||
operator: '!=',
|
||||
};
|
||||
|
||||
const variable = variableBuilder
|
||||
.adHoc()
|
||||
.withUUID(uuid)
|
||||
.withFilters([existing])
|
||||
const variable = adHocBuilder()
|
||||
.withId('elastic-filter')
|
||||
.withName('elastic-filter')
|
||||
.withFilters([existing])
|
||||
.withDatasource('elasticsearch')
|
||||
.build();
|
||||
|
||||
@@ -234,20 +205,15 @@ describe('adhoc actions', () => {
|
||||
const tester = await reduxTester<ReducersUsedInContext>()
|
||||
.givenRootReducer(getRootReducer())
|
||||
.whenActionIsDispatched(createAddVariableAction(variable))
|
||||
.whenAsyncActionIsDispatched(changeFilter(uuid, update), true);
|
||||
.whenAsyncActionIsDispatched(changeFilter('elastic-filter', update), true);
|
||||
|
||||
const expectedQuery = { 'var-elastic-filter': ['key|!=|value'] };
|
||||
const expectedUpdate = { index: 0, filter: updated };
|
||||
|
||||
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
|
||||
const [filterUpdatedAction, updateLocationAction] = actions;
|
||||
const expectedNumberOfActions = 2;
|
||||
|
||||
expect(filterUpdatedAction).toEqual(filterUpdated(toVariablePayload(variable, expectedUpdate)));
|
||||
expect(updateLocationAction).toEqual(updateLocation({ query: expectedQuery }));
|
||||
|
||||
return actions.length === expectedNumberOfActions;
|
||||
});
|
||||
tester.thenDispatchedActionsShouldEqual(
|
||||
filterUpdated(toVariablePayload(variable, expectedUpdate)),
|
||||
updateLocation({ query: expectedQuery })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -265,31 +231,25 @@ describe('adhoc actions', () => {
|
||||
operator: '!=',
|
||||
};
|
||||
|
||||
const variable = variableBuilder
|
||||
.adHoc()
|
||||
.withUUID(uuid)
|
||||
.withFilters([existing])
|
||||
const variable = adHocBuilder()
|
||||
.withId('elastic-filter')
|
||||
.withName('elastic-filter')
|
||||
.withFilters([existing])
|
||||
.withDatasource('elasticsearch')
|
||||
.build();
|
||||
|
||||
const tester = await reduxTester<ReducersUsedInContext>()
|
||||
.givenRootReducer(getRootReducer())
|
||||
.whenActionIsDispatched(createAddVariableAction(variable))
|
||||
.whenAsyncActionIsDispatched(addFilter(uuid, adding), true);
|
||||
.whenAsyncActionIsDispatched(addFilter('elastic-filter', adding), true);
|
||||
|
||||
const expectedQuery = { 'var-elastic-filter': ['key|=|value', 'key|!=|value'] };
|
||||
const expectedFilter = { key: 'key', value: 'value', operator: '!=', condition: '' };
|
||||
|
||||
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
|
||||
const [filterAddAction, updateLocationAction] = actions;
|
||||
const expectedNumberOfActions = 2;
|
||||
|
||||
expect(filterAddAction).toEqual(filterAdded(toVariablePayload(variable, expectedFilter)));
|
||||
expect(updateLocationAction).toEqual(updateLocation({ query: expectedQuery }));
|
||||
|
||||
return actions.length === expectedNumberOfActions;
|
||||
});
|
||||
tester.thenDispatchedActionsShouldEqual(
|
||||
filterAdded(toVariablePayload(variable, expectedFilter)),
|
||||
updateLocation({ query: expectedQuery })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -302,59 +262,47 @@ describe('adhoc actions', () => {
|
||||
condition: '',
|
||||
};
|
||||
|
||||
const variable = variableBuilder
|
||||
.adHoc()
|
||||
.withUUID(uuid)
|
||||
.withFilters([])
|
||||
const variable = adHocBuilder()
|
||||
.withId('elastic-filter')
|
||||
.withName('elastic-filter')
|
||||
.withFilters([])
|
||||
.withDatasource('elasticsearch')
|
||||
.build();
|
||||
|
||||
const tester = await reduxTester<ReducersUsedInContext>()
|
||||
.givenRootReducer(getRootReducer())
|
||||
.whenActionIsDispatched(createAddVariableAction(variable))
|
||||
.whenAsyncActionIsDispatched(addFilter(uuid, adding), true);
|
||||
.whenAsyncActionIsDispatched(addFilter('elastic-filter', adding), true);
|
||||
|
||||
const expectedQuery = { 'var-elastic-filter': ['key|=|value'] };
|
||||
|
||||
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
|
||||
const [filterAddAction, updateLocationAction] = actions;
|
||||
const expectedNumberOfActions = 2;
|
||||
|
||||
expect(filterAddAction).toEqual(filterAdded(toVariablePayload(variable, adding)));
|
||||
expect(updateLocationAction).toEqual(updateLocation({ query: expectedQuery }));
|
||||
|
||||
return actions.length === expectedNumberOfActions;
|
||||
});
|
||||
tester.thenDispatchedActionsShouldEqual(
|
||||
filterAdded(toVariablePayload(variable, adding)),
|
||||
updateLocation({ query: expectedQuery })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when removeFilter is dispatched on variable with no existing filter', () => {
|
||||
it('then correct actions are dispatched', async () => {
|
||||
const variable = variableBuilder
|
||||
.adHoc()
|
||||
.withUUID(uuid)
|
||||
.withFilters([])
|
||||
const variable = adHocBuilder()
|
||||
.withId('elastic-filter')
|
||||
.withName('elastic-filter')
|
||||
.withFilters([])
|
||||
.withDatasource('elasticsearch')
|
||||
.build();
|
||||
|
||||
const tester = await reduxTester<ReducersUsedInContext>()
|
||||
.givenRootReducer(getRootReducer())
|
||||
.whenActionIsDispatched(createAddVariableAction(variable))
|
||||
.whenAsyncActionIsDispatched(removeFilter(uuid, 0), true);
|
||||
.whenAsyncActionIsDispatched(removeFilter('elastic-filter', 0), true);
|
||||
|
||||
const expectedQuery = { 'var-elastic-filter': [] as string[] };
|
||||
|
||||
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
|
||||
const [filterRemoveAction, updateLocationAction] = actions;
|
||||
const expectedNumberOfActions = 2;
|
||||
|
||||
expect(filterRemoveAction).toEqual(filterRemoved(toVariablePayload(variable, 0)));
|
||||
expect(updateLocationAction).toEqual(updateLocation({ query: expectedQuery }));
|
||||
|
||||
return actions.length === expectedNumberOfActions;
|
||||
});
|
||||
tester.thenDispatchedActionsShouldEqual(
|
||||
filterRemoved(toVariablePayload(variable, 0)),
|
||||
updateLocation({ query: expectedQuery })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -367,30 +315,24 @@ describe('adhoc actions', () => {
|
||||
condition: '',
|
||||
};
|
||||
|
||||
const variable = variableBuilder
|
||||
.adHoc()
|
||||
.withUUID(uuid)
|
||||
.withFilters([filter])
|
||||
const variable = adHocBuilder()
|
||||
.withId('elastic-filter')
|
||||
.withName('elastic-filter')
|
||||
.withFilters([filter])
|
||||
.withDatasource('elasticsearch')
|
||||
.build();
|
||||
|
||||
const tester = await reduxTester<ReducersUsedInContext>()
|
||||
.givenRootReducer(getRootReducer())
|
||||
.whenActionIsDispatched(createAddVariableAction(variable))
|
||||
.whenAsyncActionIsDispatched(removeFilter(uuid, 0), true);
|
||||
.whenAsyncActionIsDispatched(removeFilter('elastic-filter', 0), true);
|
||||
|
||||
const expectedQuery = { 'var-elastic-filter': [] as string[] };
|
||||
|
||||
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
|
||||
const [filterRemoveAction, updateLocationAction] = actions;
|
||||
const expectedNumberOfActions = 2;
|
||||
|
||||
expect(filterRemoveAction).toEqual(filterRemoved(toVariablePayload(variable, 0)));
|
||||
expect(updateLocationAction).toEqual(updateLocation({ query: expectedQuery }));
|
||||
|
||||
return actions.length === expectedNumberOfActions;
|
||||
});
|
||||
tester.thenDispatchedActionsShouldEqual(
|
||||
filterRemoved(toVariablePayload(variable, 0)),
|
||||
updateLocation({ query: expectedQuery })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -403,11 +345,10 @@ describe('adhoc actions', () => {
|
||||
condition: '',
|
||||
};
|
||||
|
||||
const variable = variableBuilder
|
||||
.adHoc()
|
||||
.withUUID(uuid)
|
||||
.withFilters([existing])
|
||||
const variable = adHocBuilder()
|
||||
.withId('elastic-filter')
|
||||
.withName('elastic-filter')
|
||||
.withFilters([existing])
|
||||
.withDatasource('elasticsearch')
|
||||
.build();
|
||||
|
||||
@@ -419,7 +360,7 @@ describe('adhoc actions', () => {
|
||||
const tester = await reduxTester<ReducersUsedInContext>()
|
||||
.givenRootReducer(getRootReducer())
|
||||
.whenActionIsDispatched(createAddVariableAction(variable))
|
||||
.whenAsyncActionIsDispatched(setFiltersFromUrl(uuid, fromUrl), true);
|
||||
.whenAsyncActionIsDispatched(setFiltersFromUrl('elastic-filter', fromUrl), true);
|
||||
|
||||
const expectedQuery = { 'var-elastic-filter': ['key|=|value', 'key|=|value'] };
|
||||
const expectedFilters = [
|
||||
@@ -427,15 +368,10 @@ describe('adhoc actions', () => {
|
||||
{ key: 'key', value: 'value', operator: '=', condition: '', name: 'value-2' },
|
||||
];
|
||||
|
||||
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
|
||||
const [filterRestoredAction, updateLocationAction] = actions;
|
||||
const expectedNumberOfActions = 2;
|
||||
|
||||
expect(filterRestoredAction).toEqual(filtersRestored(toVariablePayload(variable, expectedFilters)));
|
||||
expect(updateLocationAction).toEqual(updateLocation({ query: expectedQuery }));
|
||||
|
||||
return actions.length === expectedNumberOfActions;
|
||||
});
|
||||
tester.thenDispatchedActionsShouldEqual(
|
||||
filtersRestored(toVariablePayload(variable, expectedFilters)),
|
||||
updateLocation({ query: expectedQuery })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -463,19 +399,9 @@ describe('adhoc actions', () => {
|
||||
{ text: 'elasticsearch-v7', value: 'elasticsearch-v7' },
|
||||
];
|
||||
|
||||
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
|
||||
const [changeEditorAction] = actions;
|
||||
const expectedNumberOfActions = 1;
|
||||
|
||||
expect(changeEditorAction).toEqual(
|
||||
changeVariableEditorExtended({
|
||||
propName: 'dataSources',
|
||||
propValue: expectedDatasources,
|
||||
})
|
||||
);
|
||||
|
||||
return actions.length === expectedNumberOfActions;
|
||||
});
|
||||
tester.thenDispatchedActionsShouldEqual(
|
||||
changeVariableEditorExtended({ propName: 'dataSources', propValue: expectedDatasources })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -483,9 +409,9 @@ describe('adhoc actions', () => {
|
||||
it('then correct actions are dispatched', async () => {
|
||||
const datasource = 'mysql';
|
||||
const loadingText = 'Adhoc filters are applied automatically to all queries that target this datasource';
|
||||
const variable = variableBuilder
|
||||
.adHoc()
|
||||
.withUUID(uuid)
|
||||
const variable = adHocBuilder()
|
||||
.withId('Filters')
|
||||
.withName('Filters')
|
||||
.withDatasource('influxdb')
|
||||
.build();
|
||||
|
||||
@@ -495,28 +421,17 @@ describe('adhoc actions', () => {
|
||||
const tester = await reduxTester<ReducersUsedInContext>()
|
||||
.givenRootReducer(getRootReducer())
|
||||
.whenActionIsDispatched(createAddVariableAction(variable))
|
||||
.whenActionIsDispatched(setIdInEditor({ id: variable.uuid! }))
|
||||
.whenActionIsDispatched(setIdInEditor({ id: variable.id! }))
|
||||
.whenAsyncActionIsDispatched(changeVariableDatasource(datasource), true);
|
||||
|
||||
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
|
||||
const [loadingTextAction, changePropAction, unsupportedTextAction] = actions;
|
||||
const expectedNumberOfActions = 3;
|
||||
|
||||
expect(loadingTextAction).toEqual(
|
||||
changeVariableEditorExtended({ propName: 'infoText', propValue: loadingText })
|
||||
);
|
||||
expect(changePropAction).toEqual(
|
||||
changeVariableProp(toVariablePayload(variable, { propName: 'datasource', propValue: datasource }))
|
||||
);
|
||||
expect(unsupportedTextAction).toEqual(
|
||||
changeVariableEditorExtended({
|
||||
propName: 'infoText',
|
||||
propValue: 'This datasource does not support adhoc filters yet.',
|
||||
})
|
||||
);
|
||||
|
||||
return actions.length === expectedNumberOfActions;
|
||||
});
|
||||
tester.thenDispatchedActionsShouldEqual(
|
||||
changeVariableEditorExtended({ propName: 'infoText', propValue: loadingText }),
|
||||
changeVariableProp(toVariablePayload(variable, { propName: 'datasource', propValue: datasource })),
|
||||
changeVariableEditorExtended({
|
||||
propName: 'infoText',
|
||||
propValue: 'This datasource does not support adhoc filters yet.',
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -524,9 +439,9 @@ describe('adhoc actions', () => {
|
||||
it('then correct actions are dispatched', async () => {
|
||||
const datasource = 'elasticsearch';
|
||||
const loadingText = 'Adhoc filters are applied automatically to all queries that target this datasource';
|
||||
const variable = variableBuilder
|
||||
.adHoc()
|
||||
.withUUID(uuid)
|
||||
const variable = adHocBuilder()
|
||||
.withId('Filters')
|
||||
.withName('Filters')
|
||||
.withDatasource('influxdb')
|
||||
.build();
|
||||
|
||||
@@ -538,22 +453,13 @@ describe('adhoc actions', () => {
|
||||
const tester = await reduxTester<ReducersUsedInContext>()
|
||||
.givenRootReducer(getRootReducer())
|
||||
.whenActionIsDispatched(createAddVariableAction(variable))
|
||||
.whenActionIsDispatched(setIdInEditor({ id: variable.uuid! }))
|
||||
.whenActionIsDispatched(setIdInEditor({ id: variable.id! }))
|
||||
.whenAsyncActionIsDispatched(changeVariableDatasource(datasource), true);
|
||||
|
||||
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
|
||||
const [loadingTextAction, changePropAction] = actions;
|
||||
const expectedNumberOfActions = 2;
|
||||
|
||||
expect(loadingTextAction).toEqual(
|
||||
changeVariableEditorExtended({ propName: 'infoText', propValue: loadingText })
|
||||
);
|
||||
expect(changePropAction).toEqual(
|
||||
changeVariableProp(toVariablePayload(variable, { propName: 'datasource', propValue: datasource }))
|
||||
);
|
||||
|
||||
return actions.length === expectedNumberOfActions;
|
||||
});
|
||||
tester.thenDispatchedActionsShouldEqual(
|
||||
changeVariableEditorExtended({ propName: 'infoText', propValue: loadingText }),
|
||||
changeVariableProp(toVariablePayload(variable, { propName: 'datasource', propValue: datasource }))
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,17 +1,16 @@
|
||||
import { v4 } from 'uuid';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { ThunkResult, StoreState } from 'app/types';
|
||||
import { StoreState, ThunkResult } from 'app/types';
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
import { changeVariableEditorExtended } from '../editor/reducer';
|
||||
import { changeVariableProp, addVariable } from '../state/sharedReducer';
|
||||
import { getVariable, getNewVariabelIndex } from '../state/selectors';
|
||||
import { toVariablePayload, toVariableIdentifier, AddVariable, VariableIdentifier } from '../state/types';
|
||||
import { addVariable, changeVariableProp } from '../state/sharedReducer';
|
||||
import { getNewVariabelIndex, getVariable } from '../state/selectors';
|
||||
import { AddVariable, toVariableIdentifier, toVariablePayload, VariableIdentifier } from '../state/types';
|
||||
import {
|
||||
AdHocVariabelFilterUpdate,
|
||||
filterRemoved,
|
||||
filterUpdated,
|
||||
filterAdded,
|
||||
filterRemoved,
|
||||
filtersRestored,
|
||||
filterUpdated,
|
||||
initialAdHocVariableModelState,
|
||||
} from './reducer';
|
||||
import { AdHocVariableFilter, AdHocVariableModel } from 'app/features/templating/variable';
|
||||
@@ -41,41 +40,41 @@ export const applyFilterFromTable = (options: AdHocTableOptions): ThunkResult<vo
|
||||
if (index === -1) {
|
||||
const { value, key, operator } = options;
|
||||
const filter = { value, key, operator, condition: '' };
|
||||
return await dispatch(addFilter(variable.uuid!, filter));
|
||||
return await dispatch(addFilter(variable.id!, filter));
|
||||
}
|
||||
|
||||
const filter = { ...variable.filters[index], operator: options.operator };
|
||||
return await dispatch(changeFilter(variable.uuid!, { index, filter }));
|
||||
return await dispatch(changeFilter(variable.id!, { index, filter }));
|
||||
};
|
||||
};
|
||||
|
||||
export const changeFilter = (uuid: string, update: AdHocVariabelFilterUpdate): ThunkResult<void> => {
|
||||
export const changeFilter = (id: string, update: AdHocVariabelFilterUpdate): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
const variable = getVariable(uuid, getState());
|
||||
const variable = getVariable(id, getState());
|
||||
dispatch(filterUpdated(toVariablePayload(variable, update)));
|
||||
await dispatch(variableUpdated(toVariableIdentifier(variable), true));
|
||||
};
|
||||
};
|
||||
|
||||
export const removeFilter = (uuid: string, index: number): ThunkResult<void> => {
|
||||
export const removeFilter = (id: string, index: number): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
const variable = getVariable(uuid, getState());
|
||||
const variable = getVariable(id, getState());
|
||||
dispatch(filterRemoved(toVariablePayload(variable, index)));
|
||||
await dispatch(variableUpdated(toVariableIdentifier(variable), true));
|
||||
};
|
||||
};
|
||||
|
||||
export const addFilter = (uuid: string, filter: AdHocVariableFilter): ThunkResult<void> => {
|
||||
export const addFilter = (id: string, filter: AdHocVariableFilter): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
const variable = getVariable(uuid, getState());
|
||||
const variable = getVariable(id, getState());
|
||||
dispatch(filterAdded(toVariablePayload(variable, filter)));
|
||||
await dispatch(variableUpdated(toVariableIdentifier(variable), true));
|
||||
};
|
||||
};
|
||||
|
||||
export const setFiltersFromUrl = (uuid: string, filters: AdHocVariableFilter[]): ThunkResult<void> => {
|
||||
export const setFiltersFromUrl = (id: string, filters: AdHocVariableFilter[]): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
const variable = getVariable(uuid, getState());
|
||||
const variable = getVariable(id, getState());
|
||||
dispatch(filtersRestored(toVariablePayload(variable, filters)));
|
||||
await dispatch(variableUpdated(toVariableIdentifier(variable), true));
|
||||
};
|
||||
@@ -141,12 +140,12 @@ const createAdHocVariable = (options: AdHocTableOptions): ThunkResult<void> => {
|
||||
...cloneDeep(initialAdHocVariableModelState),
|
||||
datasource: options.datasource,
|
||||
name: filterTableName,
|
||||
uuid: v4(),
|
||||
id: filterTableName,
|
||||
};
|
||||
|
||||
const global = false;
|
||||
const index = getNewVariabelIndex(getState());
|
||||
const identifier: VariableIdentifier = { type: 'adhoc', uuid: model.uuid };
|
||||
const identifier: VariableIdentifier = { type: 'adhoc', id: model.id };
|
||||
|
||||
dispatch(
|
||||
addVariable(
|
||||
|
@@ -22,11 +22,11 @@ export const createAdHocVariableAdapter = (): VariableAdapter<AdHocVariableModel
|
||||
setValue: noop,
|
||||
setValueFromUrl: async (variable, urlValue) => {
|
||||
const filters = urlParser.toFilters(urlValue);
|
||||
await dispatch(setFiltersFromUrl(variable.uuid!, filters));
|
||||
await dispatch(setFiltersFromUrl(variable.id!, filters));
|
||||
},
|
||||
updateOptions: noop,
|
||||
getSaveModel: variable => {
|
||||
const { index, uuid, initLock, global, ...rest } = cloneDeep(variable);
|
||||
const { index, id, initLock, global, ...rest } = cloneDeep(variable);
|
||||
return rest;
|
||||
},
|
||||
getValueForUrl: variable => {
|
||||
|
@@ -1,15 +1,15 @@
|
||||
import React, { PureComponent, ReactNode } from 'react';
|
||||
import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux';
|
||||
import { StoreState } from 'app/types';
|
||||
import { AdHocVariableModel, AdHocVariableFilter } from 'app/features/templating/variable';
|
||||
import { AdHocVariableFilter, AdHocVariableModel } from 'app/features/templating/variable';
|
||||
import { SegmentAsync } from '@grafana/ui';
|
||||
import { VariablePickerProps } from '../../pickers/types';
|
||||
import { OperatorSegment } from './OperatorSegment';
|
||||
import { SelectableValue, MetricFindValue } from '@grafana/data';
|
||||
import { MetricFindValue, SelectableValue } from '@grafana/data';
|
||||
import { AdHocFilterBuilder } from './AdHocFilterBuilder';
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
import { ConditionSegment } from './ConditionSegment';
|
||||
import { addFilter, removeFilter, changeFilter } from '../actions';
|
||||
import { addFilter, changeFilter, removeFilter } from '../actions';
|
||||
|
||||
interface OwnProps extends VariablePickerProps<AdHocVariableModel> {}
|
||||
|
||||
@@ -27,14 +27,14 @@ const REMOVE_FILTER_KEY = '-- remove filter --';
|
||||
const REMOVE_VALUE = { label: REMOVE_FILTER_KEY, value: REMOVE_FILTER_KEY };
|
||||
export class AdHocPickerUnconnected extends PureComponent<Props> {
|
||||
onChange = (index: number, prop: string) => (key: SelectableValue<string>) => {
|
||||
const { uuid, filters } = this.props.variable;
|
||||
const { id, filters } = this.props.variable;
|
||||
const { value } = key;
|
||||
|
||||
if (key.value === REMOVE_FILTER_KEY) {
|
||||
return this.props.removeFilter(uuid!, index);
|
||||
return this.props.removeFilter(id!, index);
|
||||
}
|
||||
|
||||
return this.props.changeFilter(uuid!, {
|
||||
return this.props.changeFilter(id!, {
|
||||
index,
|
||||
filter: {
|
||||
...filters[index],
|
||||
@@ -44,8 +44,8 @@ export class AdHocPickerUnconnected extends PureComponent<Props> {
|
||||
};
|
||||
|
||||
appendFilterToVariable = (filter: AdHocVariableFilter) => {
|
||||
const { uuid } = this.props.variable;
|
||||
this.props.addFilter(uuid!, filter);
|
||||
const { id } = this.props.variable;
|
||||
this.props.addFilter(id!, filter);
|
||||
};
|
||||
|
||||
fetchFilterKeys = async () => {
|
||||
|
@@ -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 { adHocVariableReducer, filterAdded, filterRemoved, filterUpdated, filtersRestored } from './reducer';
|
||||
import { adHocVariableReducer, filterAdded, filterRemoved, filtersRestored, filterUpdated } from './reducer';
|
||||
import { VariablesState } from '../state/variablesReducer';
|
||||
import { AdHocVariableModel, AdHocVariableFilter } from '../../templating/variable';
|
||||
import { AdHocVariableFilter, AdHocVariableModel } from '../../templating/variable';
|
||||
import { createAdHocVariableAdapter } from './adapter';
|
||||
|
||||
describe('adHocVariableReducer', () => {
|
||||
@@ -12,17 +12,17 @@ describe('adHocVariableReducer', () => {
|
||||
|
||||
describe('when filterAdded is dispatched', () => {
|
||||
it('then state should be correct', () => {
|
||||
const uuid = '0';
|
||||
const { initialState } = getVariableTestContext(adapter, { uuid });
|
||||
const id = '0';
|
||||
const { initialState } = getVariableTestContext(adapter, { id });
|
||||
const filter = createFilter('a');
|
||||
const payload = toVariablePayload({ uuid, type: 'adhoc' }, filter);
|
||||
const payload = toVariablePayload({ id, type: 'adhoc' }, filter);
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(adHocVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(filterAdded(payload))
|
||||
.thenStateShouldEqual({
|
||||
[uuid]: {
|
||||
...initialState[uuid],
|
||||
[id]: {
|
||||
...initialState[id],
|
||||
filters: [{ value: 'a', operator: '=', condition: '', key: 'a' }],
|
||||
} as AdHocVariableModel,
|
||||
});
|
||||
@@ -31,18 +31,18 @@ describe('adHocVariableReducer', () => {
|
||||
|
||||
describe('when filterAdded is dispatched and filter already exists', () => {
|
||||
it('then state should be correct', () => {
|
||||
const uuid = '0';
|
||||
const id = '0';
|
||||
const filterA = createFilter('a');
|
||||
const filterB = createFilter('b');
|
||||
const { initialState } = getVariableTestContext(adapter, { uuid, filters: [filterA] });
|
||||
const payload = toVariablePayload({ uuid, type: 'adhoc' }, filterB);
|
||||
const { initialState } = getVariableTestContext(adapter, { id, filters: [filterA] });
|
||||
const payload = toVariablePayload({ id, type: 'adhoc' }, filterB);
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(adHocVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(filterAdded(payload))
|
||||
.thenStateShouldEqual({
|
||||
[uuid]: {
|
||||
...initialState[uuid],
|
||||
[id]: {
|
||||
...initialState[id],
|
||||
filters: [
|
||||
{ value: 'a', operator: '=', condition: '', key: 'a' },
|
||||
{ value: 'b', operator: '=', condition: '', key: 'b' },
|
||||
@@ -54,19 +54,19 @@ describe('adHocVariableReducer', () => {
|
||||
|
||||
describe('when filterRemoved is dispatched to remove second filter', () => {
|
||||
it('then state should be correct', () => {
|
||||
const uuid = '0';
|
||||
const id = '0';
|
||||
const filterA = createFilter('a');
|
||||
const filterB = createFilter('b');
|
||||
const index = 1;
|
||||
const { initialState } = getVariableTestContext(adapter, { uuid, filters: [filterA, filterB] });
|
||||
const payload = toVariablePayload({ uuid, type: 'adhoc' }, index);
|
||||
const { initialState } = getVariableTestContext(adapter, { id, filters: [filterA, filterB] });
|
||||
const payload = toVariablePayload({ id, type: 'adhoc' }, index);
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(adHocVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(filterRemoved(payload))
|
||||
.thenStateShouldEqual({
|
||||
[uuid]: {
|
||||
...initialState[uuid],
|
||||
[id]: {
|
||||
...initialState[id],
|
||||
filters: [{ value: 'a', operator: '=', condition: '', key: 'a' }],
|
||||
} as AdHocVariableModel,
|
||||
});
|
||||
@@ -75,19 +75,19 @@ describe('adHocVariableReducer', () => {
|
||||
|
||||
describe('when filterRemoved is dispatched to remove first filter', () => {
|
||||
it('then state should be correct', () => {
|
||||
const uuid = '0';
|
||||
const id = '0';
|
||||
const filterA = createFilter('a');
|
||||
const filterB = createFilter('b');
|
||||
const index = 0;
|
||||
const { initialState } = getVariableTestContext(adapter, { uuid, filters: [filterA, filterB] });
|
||||
const payload = toVariablePayload({ uuid, type: 'adhoc' }, index);
|
||||
const { initialState } = getVariableTestContext(adapter, { id, filters: [filterA, filterB] });
|
||||
const payload = toVariablePayload({ id, type: 'adhoc' }, index);
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(adHocVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(filterRemoved(payload))
|
||||
.thenStateShouldEqual({
|
||||
[uuid]: {
|
||||
...initialState[uuid],
|
||||
[id]: {
|
||||
...initialState[id],
|
||||
filters: [{ value: 'b', operator: '=', condition: '', key: 'b' }],
|
||||
} as AdHocVariableModel,
|
||||
});
|
||||
@@ -96,18 +96,18 @@ describe('adHocVariableReducer', () => {
|
||||
|
||||
describe('when filterRemoved is dispatched to all filters', () => {
|
||||
it('then state should be correct', () => {
|
||||
const uuid = '0';
|
||||
const id = '0';
|
||||
const filterA = createFilter('a');
|
||||
const index = 0;
|
||||
const { initialState } = getVariableTestContext(adapter, { uuid, filters: [filterA] });
|
||||
const payload = toVariablePayload({ uuid, type: 'adhoc' }, index);
|
||||
const { initialState } = getVariableTestContext(adapter, { id, filters: [filterA] });
|
||||
const payload = toVariablePayload({ id, type: 'adhoc' }, index);
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(adHocVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(filterRemoved(payload))
|
||||
.thenStateShouldEqual({
|
||||
[uuid]: {
|
||||
...initialState[uuid],
|
||||
[id]: {
|
||||
...initialState[id],
|
||||
filters: [] as AdHocVariableFilter[],
|
||||
} as AdHocVariableModel,
|
||||
});
|
||||
@@ -116,20 +116,20 @@ describe('adHocVariableReducer', () => {
|
||||
|
||||
describe('when filterUpdated is dispatched', () => {
|
||||
it('then state should be correct', () => {
|
||||
const uuid = '0';
|
||||
const id = '0';
|
||||
const original = createFilter('a');
|
||||
const other = createFilter('b');
|
||||
const filter = createFilter('aa');
|
||||
const index = 1;
|
||||
const { initialState } = getVariableTestContext(adapter, { uuid, filters: [other, original] });
|
||||
const payload = toVariablePayload({ uuid, type: 'adhoc' }, { index, filter });
|
||||
const { initialState } = getVariableTestContext(adapter, { id, filters: [other, original] });
|
||||
const payload = toVariablePayload({ id, type: 'adhoc' }, { index, filter });
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(adHocVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(filterUpdated(payload))
|
||||
.thenStateShouldEqual({
|
||||
[uuid]: {
|
||||
...initialState[uuid],
|
||||
[id]: {
|
||||
...initialState[id],
|
||||
filters: [
|
||||
{ value: 'b', operator: '=', condition: '', key: 'b' },
|
||||
{ value: 'aa', operator: '=', condition: '', key: 'aa' },
|
||||
@@ -141,20 +141,20 @@ describe('adHocVariableReducer', () => {
|
||||
|
||||
describe('when filterUpdated is dispatched to update operator', () => {
|
||||
it('then state should be correct', () => {
|
||||
const uuid = '0';
|
||||
const id = '0';
|
||||
const original = createFilter('a');
|
||||
const other = createFilter('b');
|
||||
const filter = createFilter('aa', '>');
|
||||
const index = 1;
|
||||
const { initialState } = getVariableTestContext(adapter, { uuid, filters: [other, original] });
|
||||
const payload = toVariablePayload({ uuid, type: 'adhoc' }, { index, filter });
|
||||
const { initialState } = getVariableTestContext(adapter, { id, filters: [other, original] });
|
||||
const payload = toVariablePayload({ id, type: 'adhoc' }, { index, filter });
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(adHocVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(filterUpdated(payload))
|
||||
.thenStateShouldEqual({
|
||||
[uuid]: {
|
||||
...initialState[uuid],
|
||||
[id]: {
|
||||
...initialState[id],
|
||||
filters: [
|
||||
{ value: 'b', operator: '=', condition: '', key: 'b' },
|
||||
{ value: 'aa', operator: '>', condition: '', key: 'aa' },
|
||||
@@ -166,18 +166,18 @@ describe('adHocVariableReducer', () => {
|
||||
|
||||
describe('when filtersRestored is dispatched', () => {
|
||||
it('then state should be correct', () => {
|
||||
const uuid = '0';
|
||||
const id = '0';
|
||||
const original = [createFilter('a'), createFilter('b')];
|
||||
const restored = [createFilter('aa'), createFilter('bb')];
|
||||
const { initialState } = getVariableTestContext(adapter, { uuid, filters: original });
|
||||
const payload = toVariablePayload({ uuid, type: 'adhoc' }, restored);
|
||||
const { initialState } = getVariableTestContext(adapter, { id, filters: original });
|
||||
const payload = toVariablePayload({ id, type: 'adhoc' }, restored);
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(adHocVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(filtersRestored(payload))
|
||||
.thenStateShouldEqual({
|
||||
[uuid]: {
|
||||
...initialState[uuid],
|
||||
[id]: {
|
||||
...initialState[id],
|
||||
filters: [
|
||||
{ value: 'aa', operator: '=', condition: '', key: 'aa' },
|
||||
{ value: 'bb', operator: '=', condition: '', key: 'bb' },
|
||||
@@ -189,17 +189,17 @@ describe('adHocVariableReducer', () => {
|
||||
|
||||
describe('when filtersRestored is dispatched on variabel with no filters', () => {
|
||||
it('then state should be correct', () => {
|
||||
const uuid = '0';
|
||||
const id = '0';
|
||||
const restored = [createFilter('aa'), createFilter('bb')];
|
||||
const { initialState } = getVariableTestContext(adapter, { uuid });
|
||||
const payload = toVariablePayload({ uuid, type: 'adhoc' }, restored);
|
||||
const { initialState } = getVariableTestContext(adapter, { id });
|
||||
const payload = toVariablePayload({ id, type: 'adhoc' }, restored);
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(adHocVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(filtersRestored(payload))
|
||||
.thenStateShouldEqual({
|
||||
[uuid]: {
|
||||
...initialState[uuid],
|
||||
[id]: {
|
||||
...initialState[id],
|
||||
filters: [
|
||||
{ value: 'aa', operator: '=', condition: '', key: 'aa' },
|
||||
{ value: 'bb', operator: '=', condition: '', key: 'bb' },
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { AdHocVariableModel, VariableHide, AdHocVariableFilter } from 'app/features/templating/variable';
|
||||
import { EMPTY_UUID, getInstanceState, VariablePayload } from '../state/types';
|
||||
import { PayloadAction, createSlice } from '@reduxjs/toolkit';
|
||||
import { VariablesState, initialVariablesState } from '../state/variablesReducer';
|
||||
import { AdHocVariableFilter, AdHocVariableModel, VariableHide } from 'app/features/templating/variable';
|
||||
import { getInstanceState, NEW_VARIABLE_ID, VariablePayload } from '../state/types';
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { initialVariablesState, VariablesState } from '../state/variablesReducer';
|
||||
|
||||
export interface AdHocVariabelFilterUpdate {
|
||||
index: number;
|
||||
@@ -13,7 +13,7 @@ export interface AdHocVariableEditorState {
|
||||
}
|
||||
|
||||
export const initialAdHocVariableModelState: AdHocVariableModel = {
|
||||
uuid: EMPTY_UUID,
|
||||
id: NEW_VARIABLE_ID,
|
||||
global: false,
|
||||
type: 'adhoc',
|
||||
name: '',
|
||||
@@ -31,23 +31,23 @@ export const adHocVariableSlice = createSlice({
|
||||
initialState: initialVariablesState,
|
||||
reducers: {
|
||||
filterAdded: (state: VariablesState, action: PayloadAction<VariablePayload<AdHocVariableFilter>>) => {
|
||||
const instanceState = getInstanceState<AdHocVariableModel>(state, action.payload.uuid);
|
||||
const instanceState = getInstanceState<AdHocVariableModel>(state, action.payload.id);
|
||||
instanceState.filters.push(action.payload.data);
|
||||
},
|
||||
filterRemoved: (state: VariablesState, action: PayloadAction<VariablePayload<number>>) => {
|
||||
const instanceState = getInstanceState<AdHocVariableModel>(state, action.payload.uuid);
|
||||
const instanceState = getInstanceState<AdHocVariableModel>(state, action.payload.id);
|
||||
const index = action.payload.data;
|
||||
|
||||
instanceState.filters.splice(index, 1);
|
||||
},
|
||||
filterUpdated: (state: VariablesState, action: PayloadAction<VariablePayload<AdHocVariabelFilterUpdate>>) => {
|
||||
const instanceState = getInstanceState<AdHocVariableModel>(state, action.payload.uuid);
|
||||
const instanceState = getInstanceState<AdHocVariableModel>(state, action.payload.id);
|
||||
const { filter, index } = action.payload.data;
|
||||
|
||||
instanceState.filters[index] = filter;
|
||||
},
|
||||
filtersRestored: (state: VariablesState, action: PayloadAction<VariablePayload<AdHocVariableFilter[]>>) => {
|
||||
const instanceState = getInstanceState<AdHocVariableModel>(state, action.payload.uuid);
|
||||
const instanceState = getInstanceState<AdHocVariableModel>(state, action.payload.id);
|
||||
instanceState.filters = action.payload.data;
|
||||
},
|
||||
},
|
||||
|
@@ -23,7 +23,7 @@ describe('constant actions', () => {
|
||||
|
||||
const variable: ConstantVariableModel = {
|
||||
type: 'constant',
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
global: false,
|
||||
current: {
|
||||
value: '',
|
||||
|
@@ -30,7 +30,7 @@ export const createConstantVariableAdapter = (): VariableAdapter<ConstantVariabl
|
||||
await dispatch(updateConstantVariableOptions(toVariableIdentifier(variable)));
|
||||
},
|
||||
getSaveModel: variable => {
|
||||
const { index, uuid, initLock, global, ...rest } = cloneDeep(variable);
|
||||
const { index, id, initLock, global, ...rest } = cloneDeep(variable);
|
||||
return rest;
|
||||
},
|
||||
getValueForUrl: variable => {
|
||||
|
@@ -13,16 +13,16 @@ describe('constantVariableReducer', () => {
|
||||
describe('when createConstantOptionsFromQuery is dispatched', () => {
|
||||
it('then state should be correct', () => {
|
||||
const query = 'ABC';
|
||||
const uuid = '0';
|
||||
const { initialState } = getVariableTestContext(adapter, { uuid, query });
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'constant' });
|
||||
const id = '0';
|
||||
const { initialState } = getVariableTestContext(adapter, { id, query });
|
||||
const payload = toVariablePayload({ id: '0', type: 'constant' });
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(constantVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(createConstantOptionsFromQuery(payload))
|
||||
.thenStateShouldEqual({
|
||||
[uuid]: {
|
||||
...initialState[uuid],
|
||||
[id]: {
|
||||
...initialState[id],
|
||||
options: [
|
||||
{
|
||||
text: query,
|
||||
@@ -38,16 +38,16 @@ describe('constantVariableReducer', () => {
|
||||
describe('when createConstantOptionsFromQuery is dispatched and query contains spaces', () => {
|
||||
it('then state should be correct', () => {
|
||||
const query = ' ABC ';
|
||||
const uuid = '0';
|
||||
const { initialState } = getVariableTestContext(adapter, { uuid, query });
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'constant' });
|
||||
const id = '0';
|
||||
const { initialState } = getVariableTestContext(adapter, { id, query });
|
||||
const payload = toVariablePayload({ id: '0', type: 'constant' });
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(constantVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(createConstantOptionsFromQuery(payload))
|
||||
.thenStateShouldEqual({
|
||||
[uuid]: {
|
||||
...initialState[uuid],
|
||||
[id]: {
|
||||
...initialState[id],
|
||||
options: [
|
||||
{
|
||||
text: query.trim(),
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { ConstantVariableModel, VariableHide, VariableOption } from '../../templating/variable';
|
||||
import { EMPTY_UUID, getInstanceState, VariablePayload } from '../state/types';
|
||||
import { getInstanceState, NEW_VARIABLE_ID, VariablePayload } from '../state/types';
|
||||
import { initialVariablesState, VariablesState } from '../state/variablesReducer';
|
||||
|
||||
export const initialConstantVariableModelState: ConstantVariableModel = {
|
||||
uuid: EMPTY_UUID,
|
||||
id: NEW_VARIABLE_ID,
|
||||
global: false,
|
||||
type: 'constant',
|
||||
name: '',
|
||||
@@ -23,7 +23,7 @@ export const constantVariableSlice = createSlice({
|
||||
initialState: initialVariablesState,
|
||||
reducers: {
|
||||
createConstantOptionsFromQuery: (state: VariablesState, action: PayloadAction<VariablePayload>) => {
|
||||
const instanceState = getInstanceState<ConstantVariableModel>(state, action.payload.uuid);
|
||||
const instanceState = getInstanceState<ConstantVariableModel>(state, action.payload.id);
|
||||
instanceState.options = [
|
||||
{ text: instanceState.query.trim(), value: instanceState.query.trim(), selected: false },
|
||||
];
|
||||
|
@@ -23,7 +23,7 @@ describe('custom actions', () => {
|
||||
|
||||
const variable: CustomVariableModel = {
|
||||
type: 'custom',
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
global: false,
|
||||
current: {
|
||||
value: '',
|
||||
|
@@ -30,7 +30,7 @@ export const createCustomVariableAdapter = (): VariableAdapter<CustomVariableMod
|
||||
await dispatch(updateCustomVariableOptions(toVariableIdentifier(variable)));
|
||||
},
|
||||
getSaveModel: variable => {
|
||||
const { index, uuid, initLock, global, ...rest } = cloneDeep(variable);
|
||||
const { index, id, initLock, global, ...rest } = cloneDeep(variable);
|
||||
return rest;
|
||||
},
|
||||
getValueForUrl: variable => {
|
||||
|
@@ -13,16 +13,16 @@ describe('customVariableReducer', () => {
|
||||
describe('when createCustomOptionsFromQuery is dispatched', () => {
|
||||
it('then state should be correct', () => {
|
||||
const query = 'a,b,c';
|
||||
const uuid = '0';
|
||||
const { initialState } = getVariableTestContext(adapter, { uuid, query });
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'custom' });
|
||||
const id = '0';
|
||||
const { initialState } = getVariableTestContext(adapter, { id, query });
|
||||
const payload = toVariablePayload({ id: '0', type: 'custom' });
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(customVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(createCustomOptionsFromQuery(payload))
|
||||
.thenStateShouldEqual({
|
||||
[uuid]: {
|
||||
...initialState[uuid],
|
||||
[id]: {
|
||||
...initialState[id],
|
||||
options: [
|
||||
{
|
||||
text: 'a',
|
||||
@@ -48,16 +48,16 @@ describe('customVariableReducer', () => {
|
||||
describe('when createCustomOptionsFromQuery is dispatched and query contains spaces', () => {
|
||||
it('then state should be correct', () => {
|
||||
const query = 'a, b, c';
|
||||
const uuid = '0';
|
||||
const { initialState } = getVariableTestContext(adapter, { uuid, query });
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'constant' });
|
||||
const id = '0';
|
||||
const { initialState } = getVariableTestContext(adapter, { id, query });
|
||||
const payload = toVariablePayload({ id: '0', type: 'constant' });
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(customVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(createCustomOptionsFromQuery(payload))
|
||||
.thenStateShouldEqual({
|
||||
[uuid]: {
|
||||
...initialState[uuid],
|
||||
[id]: {
|
||||
...initialState[id],
|
||||
options: [
|
||||
{
|
||||
text: 'a',
|
||||
@@ -83,16 +83,16 @@ describe('customVariableReducer', () => {
|
||||
describe('when createCustomOptionsFromQuery is dispatched and includeAll is true', () => {
|
||||
it('then state should be correct', () => {
|
||||
const query = 'a,b,c';
|
||||
const uuid = '0';
|
||||
const { initialState } = getVariableTestContext(adapter, { uuid, query, includeAll: true });
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'constant' });
|
||||
const id = '0';
|
||||
const { initialState } = getVariableTestContext(adapter, { id, query, includeAll: true });
|
||||
const payload = toVariablePayload({ id: '0', type: 'constant' });
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(customVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(createCustomOptionsFromQuery(payload))
|
||||
.thenStateShouldEqual({
|
||||
[uuid]: {
|
||||
...initialState[uuid],
|
||||
[id]: {
|
||||
...initialState[id],
|
||||
options: [
|
||||
{
|
||||
text: ALL_VARIABLE_TEXT,
|
||||
|
@@ -1,11 +1,17 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
import { CustomVariableModel, VariableHide, VariableOption } from '../../templating/variable';
|
||||
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, EMPTY_UUID, getInstanceState, VariablePayload } from '../state/types';
|
||||
import {
|
||||
ALL_VARIABLE_TEXT,
|
||||
ALL_VARIABLE_VALUE,
|
||||
getInstanceState,
|
||||
NEW_VARIABLE_ID,
|
||||
VariablePayload,
|
||||
} from '../state/types';
|
||||
import { initialVariablesState, VariablesState } from '../state/variablesReducer';
|
||||
|
||||
export const initialCustomVariableModelState: CustomVariableModel = {
|
||||
uuid: EMPTY_UUID,
|
||||
id: NEW_VARIABLE_ID,
|
||||
global: false,
|
||||
multi: false,
|
||||
includeAll: false,
|
||||
@@ -27,7 +33,7 @@ export const customVariableSlice = createSlice({
|
||||
initialState: initialVariablesState,
|
||||
reducers: {
|
||||
createCustomOptionsFromQuery: (state: VariablesState, action: PayloadAction<VariablePayload>) => {
|
||||
const instanceState = getInstanceState<CustomVariableModel>(state, action.payload.uuid);
|
||||
const instanceState = getInstanceState<CustomVariableModel>(state, action.payload.id);
|
||||
const { includeAll, query } = instanceState;
|
||||
const match = query.match(/(?:\\,|[^,])+/g) ?? [];
|
||||
|
||||
|
@@ -15,7 +15,7 @@ import { getMockPlugin } from '../../plugins/__mocks__/pluginMocks';
|
||||
import { createDataSourceOptions } from './reducer';
|
||||
import { setCurrentVariableValue } from '../state/sharedReducer';
|
||||
import { changeVariableEditorExtended } from '../editor/reducer';
|
||||
import * as variableBuilder from '../shared/testing/builders';
|
||||
import { datasourceBuilder } from '../shared/testing/builders';
|
||||
|
||||
describe('data source actions', () => {
|
||||
variableAdapters.set('datasource', createDataSourceVariableAdapter());
|
||||
@@ -41,9 +41,8 @@ describe('data source actions', () => {
|
||||
const getMetricSourcesMock = jest.fn().mockResolvedValue(sources);
|
||||
const getDatasourceSrvMock = jest.fn().mockReturnValue({ getMetricSources: getMetricSourcesMock });
|
||||
const dependencies: DataSourceVariableActionDependencies = { getDatasourceSrv: getDatasourceSrvMock };
|
||||
const datasource = variableBuilder
|
||||
.datasource()
|
||||
.withUUID('0')
|
||||
const datasource = datasourceBuilder()
|
||||
.withId('0')
|
||||
.withQuery('mock-data-id')
|
||||
.build();
|
||||
|
||||
@@ -57,11 +56,11 @@ describe('data source actions', () => {
|
||||
|
||||
await tester.thenDispatchedActionsShouldEqual(
|
||||
createDataSourceOptions(
|
||||
toVariablePayload({ type: 'datasource', uuid: '0' }, { sources, regex: (undefined as unknown) as RegExp })
|
||||
toVariablePayload({ type: 'datasource', id: '0' }, { sources, regex: (undefined as unknown) as RegExp })
|
||||
),
|
||||
setCurrentVariableValue(
|
||||
toVariablePayload(
|
||||
{ type: 'datasource', uuid: '0' },
|
||||
{ type: 'datasource', id: '0' },
|
||||
{ option: { text: 'first-name', value: 'first-name', selected: false } }
|
||||
)
|
||||
)
|
||||
@@ -93,9 +92,8 @@ describe('data source actions', () => {
|
||||
const getMetricSourcesMock = jest.fn().mockResolvedValue(sources);
|
||||
const getDatasourceSrvMock = jest.fn().mockReturnValue({ getMetricSources: getMetricSourcesMock });
|
||||
const dependencies: DataSourceVariableActionDependencies = { getDatasourceSrv: getDatasourceSrvMock };
|
||||
const datasource = variableBuilder
|
||||
.datasource()
|
||||
.withUUID('0')
|
||||
const datasource = datasourceBuilder()
|
||||
.withId('0')
|
||||
.withQuery('mock-data-id')
|
||||
.withRegEx('/.*(second-name).*/')
|
||||
.build();
|
||||
@@ -109,11 +107,11 @@ describe('data source actions', () => {
|
||||
|
||||
await tester.thenDispatchedActionsShouldEqual(
|
||||
createDataSourceOptions(
|
||||
toVariablePayload({ type: 'datasource', uuid: '0' }, { sources, regex: /.*(second-name).*/ })
|
||||
toVariablePayload({ type: 'datasource', id: '0' }, { sources, regex: /.*(second-name).*/ })
|
||||
),
|
||||
setCurrentVariableValue(
|
||||
toVariablePayload(
|
||||
{ type: 'datasource', uuid: '0' },
|
||||
{ type: 'datasource', id: '0' },
|
||||
{ option: { text: 'second-name', value: 'second-name', selected: false } }
|
||||
)
|
||||
)
|
||||
|
@@ -19,7 +19,7 @@ export const updateDataSourceVariableOptions = (
|
||||
dependencies: DataSourceVariableActionDependencies = { getDatasourceSrv: getDatasourceSrv }
|
||||
): ThunkResult<void> => async (dispatch, getState) => {
|
||||
const sources = await dependencies.getDatasourceSrv().getMetricSources({ skipVariables: true });
|
||||
const variableInState = getVariable<DataSourceVariableModel>(identifier.uuid!, getState());
|
||||
const variableInState = getVariable<DataSourceVariableModel>(identifier.id!, getState());
|
||||
let regex;
|
||||
|
||||
if (variableInState.regex) {
|
||||
|
@@ -33,7 +33,7 @@ export const createDataSourceVariableAdapter = (): VariableAdapter<DataSourceVar
|
||||
await dispatch(updateDataSourceVariableOptions(toVariableIdentifier(variable)));
|
||||
},
|
||||
getSaveModel: variable => {
|
||||
const { index, uuid, initLock, global, ...rest } = cloneDeep(variable);
|
||||
const { index, id, initLock, global, ...rest } = cloneDeep(variable);
|
||||
return { ...rest, options: [] };
|
||||
},
|
||||
getValueForUrl: variable => {
|
||||
|
@@ -46,7 +46,7 @@ describe('dataSourceVariableReducer', () => {
|
||||
"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 });
|
||||
const payload = toVariablePayload({ id: '0', type: 'datasource' }, { sources, regex });
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(dataSourceVariableReducer, cloneDeep(initialState))
|
||||
|
@@ -1,6 +1,12 @@
|
||||
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 {
|
||||
ALL_VARIABLE_TEXT,
|
||||
ALL_VARIABLE_VALUE,
|
||||
getInstanceState,
|
||||
NEW_VARIABLE_ID,
|
||||
VariablePayload,
|
||||
} from '../state/types';
|
||||
import { initialVariablesState, VariablesState } from '../state/variablesReducer';
|
||||
import { DataSourceSelectItem } from '@grafana/data';
|
||||
|
||||
@@ -9,7 +15,7 @@ export interface DataSourceVariableEditorState {
|
||||
}
|
||||
|
||||
export const initialDataSourceVariableModelState: DataSourceVariableModel = {
|
||||
uuid: EMPTY_UUID,
|
||||
id: NEW_VARIABLE_ID,
|
||||
global: false,
|
||||
type: 'datasource',
|
||||
name: '',
|
||||
@@ -37,7 +43,7 @@ export const dataSourceVariableSlice = createSlice({
|
||||
) => {
|
||||
const { sources, regex } = action.payload.data;
|
||||
const options: VariableOption[] = [];
|
||||
const instanceState = getInstanceState<DataSourceVariableModel>(state, action.payload.uuid);
|
||||
const instanceState = getInstanceState<DataSourceVariableModel>(state, action.payload.id);
|
||||
for (let i = 0; i < sources.length; i++) {
|
||||
const source = sources[i];
|
||||
// must match on type
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import React, { MouseEvent, PureComponent } from 'react';
|
||||
import { EMPTY_UUID, toVariableIdentifier, toVariablePayload, VariableIdentifier } from '../state/types';
|
||||
import { NEW_VARIABLE_ID, toVariableIdentifier, toVariablePayload, VariableIdentifier } from '../state/types';
|
||||
import { StoreState } from '../../../types';
|
||||
import { e2e } from '@grafana/e2e';
|
||||
import { VariableEditorList } from './VariableEditorList';
|
||||
@@ -53,7 +53,7 @@ class VariableEditorContainerUnconnected extends PureComponent<Props> {
|
||||
};
|
||||
|
||||
onDuplicateVariable = (identifier: VariableIdentifier) => {
|
||||
this.props.duplicateVariable(toVariablePayload(identifier, { newUuid: (undefined as unknown) as string }));
|
||||
this.props.duplicateVariable(toVariablePayload(identifier, { newId: (undefined as unknown) as string }));
|
||||
};
|
||||
|
||||
onRemoveVariable = (identifier: VariableIdentifier) => {
|
||||
@@ -61,7 +61,7 @@ class VariableEditorContainerUnconnected extends PureComponent<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const variableToEdit = this.props.variables.find(s => s.uuid === this.props.idInEditor) ?? null;
|
||||
const variableToEdit = this.props.variables.find(s => s.id === this.props.idInEditor) ?? null;
|
||||
return (
|
||||
<div>
|
||||
<div className="page-action-bar">
|
||||
@@ -72,7 +72,7 @@ class VariableEditorContainerUnconnected extends PureComponent<Props> {
|
||||
>
|
||||
Variables
|
||||
</a>
|
||||
{this.props.idInEditor === EMPTY_UUID && (
|
||||
{this.props.idInEditor === NEW_VARIABLE_ID && (
|
||||
<span>
|
||||
<i
|
||||
className="fa fa-fw fa-chevron-right"
|
||||
@@ -81,7 +81,7 @@ class VariableEditorContainerUnconnected extends PureComponent<Props> {
|
||||
New
|
||||
</span>
|
||||
)}
|
||||
{this.props.idInEditor && this.props.idInEditor !== EMPTY_UUID && (
|
||||
{this.props.idInEditor && this.props.idInEditor !== NEW_VARIABLE_ID && (
|
||||
<span>
|
||||
<i
|
||||
className="fa fa-fw fa-chevron-right"
|
||||
|
@@ -4,7 +4,7 @@ import { AppEvents } from '@grafana/data';
|
||||
import { FormLabel } from '@grafana/ui';
|
||||
import { e2e } from '@grafana/e2e';
|
||||
import { variableAdapters } from '../adapters';
|
||||
import { EMPTY_UUID, toVariablePayload, VariableIdentifier } from '../state/types';
|
||||
import { NEW_VARIABLE_ID, toVariablePayload, VariableIdentifier } from '../state/types';
|
||||
import { VariableHide, VariableModel, VariableType } from '../../templating/variable';
|
||||
import { appEvents } from '../../../core/core';
|
||||
import { VariableValuesPreview } from './VariableValuesPreview';
|
||||
@@ -97,11 +97,11 @@ export class VariableEditorEditorUnConnected extends PureComponent<Props> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.props.variable.uuid !== EMPTY_UUID) {
|
||||
if (this.props.variable.id !== NEW_VARIABLE_ID) {
|
||||
await this.props.onEditorUpdate(this.props.identifier);
|
||||
}
|
||||
|
||||
if (this.props.variable.uuid === EMPTY_UUID) {
|
||||
if (this.props.variable.id === NEW_VARIABLE_ID) {
|
||||
await this.props.onEditorAdd(this.props.identifier);
|
||||
}
|
||||
};
|
||||
@@ -111,7 +111,7 @@ export class VariableEditorEditorUnConnected extends PureComponent<Props> {
|
||||
if (!EditorToRender) {
|
||||
return null;
|
||||
}
|
||||
const newVariable = this.props.variable.uuid && this.props.variable.uuid === EMPTY_UUID;
|
||||
const newVariable = this.props.variable.id && this.props.variable.id === NEW_VARIABLE_ID;
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -227,7 +227,7 @@ export class VariableEditorEditorUnConnected extends PureComponent<Props> {
|
||||
|
||||
const mapStateToProps: MapStateToProps<ConnectedProps, OwnProps, StoreState> = (state, ownProps) => ({
|
||||
editor: state.templating.editor,
|
||||
variable: getVariable(ownProps.identifier.uuid!, state),
|
||||
variable: getVariable(ownProps.identifier.id, state, false), // we could be renaming a variable and we don't want this to throw
|
||||
});
|
||||
|
||||
const mapDispatchToProps: MapDispatchToProps<DispatchProps, OwnProps> = {
|
||||
|
@@ -9,30 +9,35 @@ import {
|
||||
variableEditorUnMounted,
|
||||
} from './reducer';
|
||||
import { variableAdapters } from '../adapters';
|
||||
import { v4 } from 'uuid';
|
||||
import { AddVariable, EMPTY_UUID, toVariablePayload, VariableIdentifier } from '../state/types';
|
||||
import {
|
||||
AddVariable,
|
||||
NEW_VARIABLE_ID,
|
||||
toVariableIdentifier,
|
||||
toVariablePayload,
|
||||
VariableIdentifier,
|
||||
} from '../state/types';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { VariableType } from '../../templating/variable';
|
||||
import { addVariable, removeVariable, storeNewVariable } from '../state/sharedReducer';
|
||||
|
||||
export const variableEditorMount = (identifier: VariableIdentifier): ThunkResult<void> => {
|
||||
return async dispatch => {
|
||||
dispatch(variableEditorMounted({ name: getVariable(identifier.uuid!).name }));
|
||||
dispatch(variableEditorMounted({ name: getVariable(identifier.id!).name }));
|
||||
};
|
||||
};
|
||||
|
||||
export const variableEditorUnMount = (identifier: VariableIdentifier): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch(variableEditorUnMounted(toVariablePayload(identifier)));
|
||||
if (getState().templating.variables[EMPTY_UUID]) {
|
||||
dispatch(removeVariable(toVariablePayload({ type: identifier.type, uuid: EMPTY_UUID }, { reIndex: false })));
|
||||
if (getState().templating.variables[NEW_VARIABLE_ID]) {
|
||||
dispatch(removeVariable(toVariablePayload({ type: identifier.type, id: NEW_VARIABLE_ID }, { reIndex: false })));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const onEditorUpdate = (identifier: VariableIdentifier): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
const variableInState = getVariable(identifier.uuid!, getState());
|
||||
const variableInState = getVariable(identifier.id!, getState());
|
||||
await variableAdapters.get(variableInState.type).updateOptions(variableInState);
|
||||
dispatch(switchToListMode());
|
||||
};
|
||||
@@ -40,17 +45,23 @@ export const onEditorUpdate = (identifier: VariableIdentifier): ThunkResult<void
|
||||
|
||||
export const onEditorAdd = (identifier: VariableIdentifier): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
const uuid = v4();
|
||||
dispatch(storeNewVariable(toVariablePayload({ type: identifier.type, uuid })));
|
||||
const variableInState = getVariable(uuid, getState());
|
||||
const newVariableInState = getVariable(NEW_VARIABLE_ID, getState());
|
||||
const id = newVariableInState.name;
|
||||
dispatch(storeNewVariable(toVariablePayload({ type: identifier.type, id })));
|
||||
const variableInState = getVariable(id, getState());
|
||||
await variableAdapters.get(variableInState.type).updateOptions(variableInState);
|
||||
dispatch(switchToListMode());
|
||||
dispatch(removeVariable(toVariablePayload({ type: identifier.type, uuid: EMPTY_UUID }, { reIndex: false })));
|
||||
dispatch(removeVariable(toVariablePayload({ type: identifier.type, id: NEW_VARIABLE_ID }, { reIndex: false })));
|
||||
};
|
||||
};
|
||||
|
||||
export const changeVariableName = (identifier: VariableIdentifier, newName: string): ThunkResult<void> => {
|
||||
return (dispatch, getState) => {
|
||||
const variableInState = getVariable(identifier.id, getState());
|
||||
if (newName === variableInState.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
let errorText = null;
|
||||
if (!newName.match(/^(?!__).*$/)) {
|
||||
errorText = "Template names cannot begin with '__', that's reserved for Grafana's global variables";
|
||||
@@ -61,39 +72,62 @@ export const changeVariableName = (identifier: VariableIdentifier, newName: stri
|
||||
}
|
||||
|
||||
const variables = getVariables(getState());
|
||||
const stateVariables = variables.filter(v => v.name === newName && v.uuid !== identifier.uuid);
|
||||
const foundVariables = variables.filter(v => v.name === newName && v.id !== identifier.id);
|
||||
|
||||
if (stateVariables.length) {
|
||||
if (foundVariables.length) {
|
||||
errorText = 'Variable with the same name already exists';
|
||||
}
|
||||
|
||||
if (errorText) {
|
||||
dispatch(changeVariableNameFailed({ newName, errorText }));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!errorText) {
|
||||
dispatch(changeVariableNameSucceeded(toVariablePayload(identifier, newName)));
|
||||
}
|
||||
const thunkToCall = identifier.id === NEW_VARIABLE_ID ? completeChangeNewVariableName : completeChangeVariableName;
|
||||
dispatch(thunkToCall(identifier, newName));
|
||||
};
|
||||
};
|
||||
|
||||
export const completeChangeNewVariableName = (
|
||||
identifier: VariableIdentifier,
|
||||
newName: string
|
||||
): ThunkResult<void> => dispatch => {
|
||||
dispatch(changeVariableNameSucceeded(toVariablePayload(identifier, { newName })));
|
||||
};
|
||||
|
||||
export const completeChangeVariableName = (identifier: VariableIdentifier, newName: string): ThunkResult<void> => (
|
||||
dispatch,
|
||||
getState
|
||||
) => {
|
||||
const originalVariable = getVariable(identifier.id, getState());
|
||||
const model = { ...cloneDeep(originalVariable), name: newName, id: newName };
|
||||
const global = originalVariable.global;
|
||||
const index = originalVariable.index;
|
||||
const renamedIdentifier = toVariableIdentifier(model);
|
||||
|
||||
dispatch(addVariable(toVariablePayload(renamedIdentifier, { global, index, model })));
|
||||
dispatch(changeVariableNameSucceeded(toVariablePayload(renamedIdentifier, { newName })));
|
||||
dispatch(switchToEditMode(renamedIdentifier));
|
||||
dispatch(removeVariable(toVariablePayload(identifier, { reIndex: false })));
|
||||
};
|
||||
|
||||
export const switchToNewMode = (): ThunkResult<void> => (dispatch, getState) => {
|
||||
const type: VariableType = 'query';
|
||||
const uuid = EMPTY_UUID;
|
||||
const id = NEW_VARIABLE_ID;
|
||||
const global = false;
|
||||
const model = cloneDeep(variableAdapters.get(type).initialState);
|
||||
const index = getNewVariabelIndex(getState());
|
||||
const identifier = { type, uuid };
|
||||
const identifier = { type, id };
|
||||
dispatch(
|
||||
addVariable(
|
||||
toVariablePayload<AddVariable>(identifier, { global, model, index })
|
||||
)
|
||||
);
|
||||
dispatch(setIdInEditor({ id: identifier.uuid }));
|
||||
dispatch(setIdInEditor({ id: identifier.id }));
|
||||
};
|
||||
|
||||
export const switchToEditMode = (identifier: VariableIdentifier): ThunkResult<void> => dispatch => {
|
||||
dispatch(setIdInEditor({ id: identifier.uuid }));
|
||||
dispatch(setIdInEditor({ id: identifier.id }));
|
||||
};
|
||||
|
||||
export const switchToListMode = (): ThunkResult<void> => dispatch => {
|
||||
|
@@ -63,7 +63,7 @@ describe('variableEditorReducer', () => {
|
||||
errors: { update: 'Something wrong' },
|
||||
extended: { prop: 1000 },
|
||||
};
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'textbox' });
|
||||
const payload = toVariablePayload({ id: '0', type: 'textbox' });
|
||||
reducerTester<VariableEditorState>()
|
||||
.givenReducer(variableEditorReducer, initialState)
|
||||
.whenActionIsDispatched(variableEditorUnMounted(payload))
|
||||
@@ -79,7 +79,7 @@ describe('variableEditorReducer', () => {
|
||||
isValid: false,
|
||||
errors: { name: 'Duplicate', update: 'Update failed' },
|
||||
};
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'textbox' }, 'New Name');
|
||||
const payload = toVariablePayload({ id: '0', type: 'textbox' }, { newName: 'New Name' });
|
||||
reducerTester<VariableEditorState>()
|
||||
.givenReducer(variableEditorReducer, initialState)
|
||||
.whenActionIsDispatched(changeVariableNameSucceeded(payload))
|
||||
@@ -100,7 +100,7 @@ describe('variableEditorReducer', () => {
|
||||
isValid: false,
|
||||
errors: { name: 'Duplicate' },
|
||||
};
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'textbox' }, 'New Name');
|
||||
const payload = toVariablePayload({ id: '0', type: 'textbox' }, { newName: 'New Name' });
|
||||
reducerTester<VariableEditorState>()
|
||||
.givenReducer(variableEditorReducer, initialState)
|
||||
.whenActionIsDispatched(changeVariableNameSucceeded(payload))
|
||||
|
@@ -34,8 +34,11 @@ const variableEditorReducerSlice = createSlice({
|
||||
variableEditorUnMounted: (state: VariableEditorState, action: PayloadAction<VariablePayload>) => {
|
||||
return initialVariableEditorState;
|
||||
},
|
||||
changeVariableNameSucceeded: (state: VariableEditorState, action: PayloadAction<VariablePayload<string>>) => {
|
||||
state.name = action.payload.data;
|
||||
changeVariableNameSucceeded: (
|
||||
state: VariableEditorState,
|
||||
action: PayloadAction<VariablePayload<{ newName: string }>>
|
||||
) => {
|
||||
state.name = action.payload.data.newName;
|
||||
delete state.errors['name'];
|
||||
state.isValid = Object.keys(state.errors).length === 0;
|
||||
},
|
||||
|
@@ -17,15 +17,14 @@ import { Emitter } from 'app/core/core';
|
||||
import { AppEvents, dateTime } from '@grafana/data';
|
||||
import { getTimeSrv, setTimeSrv, TimeSrv } from '../../dashboard/services/TimeSrv';
|
||||
import { TemplateSrv } from '../../templating/template_srv';
|
||||
import * as variableBuilder from '../shared/testing/builders';
|
||||
import { intervalBuilder } from '../shared/testing/builders';
|
||||
|
||||
describe('interval actions', () => {
|
||||
variableAdapters.set('interval', createIntervalVariableAdapter());
|
||||
describe('when updateIntervalVariableOptions is dispatched', () => {
|
||||
it('then correct actions are dispatched', async () => {
|
||||
const interval = variableBuilder
|
||||
.interval()
|
||||
.withUUID('0')
|
||||
const interval = intervalBuilder()
|
||||
.withId('0')
|
||||
.withQuery('1s,1m,1h,1d')
|
||||
.withAuto(false)
|
||||
.build();
|
||||
@@ -36,10 +35,10 @@ describe('interval actions', () => {
|
||||
.whenAsyncActionIsDispatched(updateIntervalVariableOptions(toVariableIdentifier(interval)), true);
|
||||
|
||||
tester.thenDispatchedActionsShouldEqual(
|
||||
createIntervalOptions({ type: 'interval', uuid: '0', data: undefined }),
|
||||
createIntervalOptions({ type: 'interval', id: '0', data: undefined }),
|
||||
setCurrentVariableValue({
|
||||
type: 'interval',
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
data: { option: { text: '1s', value: '1s', selected: false } },
|
||||
})
|
||||
);
|
||||
@@ -62,9 +61,8 @@ describe('interval actions', () => {
|
||||
} as unknown) as TimeSrv;
|
||||
const originalTimeSrv = getTimeSrv();
|
||||
setTimeSrv(timeSrvMock);
|
||||
const interval = variableBuilder
|
||||
.interval()
|
||||
.withUUID('0')
|
||||
const interval = intervalBuilder()
|
||||
.withId('0')
|
||||
.withQuery('1s,1m,1h,1d')
|
||||
.withAuto(true)
|
||||
.withAutoMin('1') // illegal interval string
|
||||
@@ -91,9 +89,8 @@ describe('interval actions', () => {
|
||||
describe('when updateAutoValue is dispatched', () => {
|
||||
describe('and auto is false', () => {
|
||||
it('then no dependencies are called', async () => {
|
||||
const interval = variableBuilder
|
||||
.interval()
|
||||
.withUUID('0')
|
||||
const interval = intervalBuilder()
|
||||
.withId('0')
|
||||
.withAuto(false)
|
||||
.build();
|
||||
|
||||
@@ -131,9 +128,8 @@ describe('interval actions', () => {
|
||||
|
||||
describe('and auto is true', () => {
|
||||
it('then correct dependencies are called', async () => {
|
||||
const interval = variableBuilder
|
||||
.interval()
|
||||
.withUUID('0')
|
||||
const interval = intervalBuilder()
|
||||
.withId('0')
|
||||
.withName('intervalName')
|
||||
.withAuto(true)
|
||||
.withAutoCount(33)
|
||||
|
@@ -42,7 +42,7 @@ export const updateAutoValue = (
|
||||
templateSrv: templateSrv,
|
||||
}
|
||||
): ThunkResult<void> => (dispatch, getState) => {
|
||||
const variableInState = getVariable<IntervalVariableModel>(identifier.uuid, getState());
|
||||
const variableInState = getVariable<IntervalVariableModel>(identifier.id, getState());
|
||||
if (variableInState.auto) {
|
||||
const res = dependencies.kbn.calculateInterval(
|
||||
dependencies.getTimeSrv().timeRange(),
|
||||
|
@@ -32,7 +32,7 @@ export const createIntervalVariableAdapter = (): VariableAdapter<IntervalVariabl
|
||||
await dispatch(updateIntervalVariableOptions(toVariableIdentifier(variable)));
|
||||
},
|
||||
getSaveModel: variable => {
|
||||
const { index, uuid, initLock, global, ...rest } = cloneDeep(variable);
|
||||
const { index, id, initLock, global, ...rest } = cloneDeep(variable);
|
||||
return rest;
|
||||
},
|
||||
getValueForUrl: variable => {
|
||||
|
@@ -13,11 +13,11 @@ describe('intervalVariableReducer', () => {
|
||||
describe('when createIntervalOptions is dispatched', () => {
|
||||
describe('and auto is false', () => {
|
||||
it('then state should be correct', () => {
|
||||
const uuid = '0';
|
||||
const id = '0';
|
||||
const query = '1s,1m,1h,1d';
|
||||
const auto = false;
|
||||
const { initialState } = getVariableTestContext<IntervalVariableModel>(adapter, { uuid, query, auto });
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'interval' });
|
||||
const { initialState } = getVariableTestContext<IntervalVariableModel>(adapter, { id, query, auto });
|
||||
const payload = toVariablePayload({ id: '0', type: 'interval' });
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(intervalVariableReducer, cloneDeep(initialState))
|
||||
@@ -25,7 +25,7 @@ describe('intervalVariableReducer', () => {
|
||||
.thenStateShouldEqual({
|
||||
'0': {
|
||||
...initialState['0'],
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
query: '1s,1m,1h,1d',
|
||||
auto: false,
|
||||
options: [
|
||||
@@ -41,11 +41,11 @@ describe('intervalVariableReducer', () => {
|
||||
|
||||
describe('and auto is true', () => {
|
||||
it('then state should be correct', () => {
|
||||
const uuid = '0';
|
||||
const id = '0';
|
||||
const query = '1s,1m,1h,1d';
|
||||
const auto = true;
|
||||
const { initialState } = getVariableTestContext<IntervalVariableModel>(adapter, { uuid, query, auto });
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'interval' });
|
||||
const { initialState } = getVariableTestContext<IntervalVariableModel>(adapter, { id, query, auto });
|
||||
const payload = toVariablePayload({ id: '0', type: 'interval' });
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(intervalVariableReducer, cloneDeep(initialState))
|
||||
@@ -53,7 +53,7 @@ describe('intervalVariableReducer', () => {
|
||||
.thenStateShouldEqual({
|
||||
'0': {
|
||||
...initialState['0'],
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
query: '1s,1m,1h,1d',
|
||||
auto: true,
|
||||
options: [
|
||||
@@ -70,11 +70,11 @@ describe('intervalVariableReducer', () => {
|
||||
|
||||
describe('and query contains "', () => {
|
||||
it('then state should be correct', () => {
|
||||
const uuid = '0';
|
||||
const id = '0';
|
||||
const query = '"kalle, anka","donald, duck"';
|
||||
const auto = false;
|
||||
const { initialState } = getVariableTestContext<IntervalVariableModel>(adapter, { uuid, query, auto });
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'interval' });
|
||||
const { initialState } = getVariableTestContext<IntervalVariableModel>(adapter, { id, query, auto });
|
||||
const payload = toVariablePayload({ id: '0', type: 'interval' });
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(intervalVariableReducer, cloneDeep(initialState))
|
||||
@@ -82,7 +82,7 @@ describe('intervalVariableReducer', () => {
|
||||
.thenStateShouldEqual({
|
||||
'0': {
|
||||
...initialState['0'],
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
query: '"kalle, anka","donald, duck"',
|
||||
auto: false,
|
||||
options: [
|
||||
@@ -96,11 +96,11 @@ describe('intervalVariableReducer', () => {
|
||||
|
||||
describe("and query contains '", () => {
|
||||
it('then state should be correct', () => {
|
||||
const uuid = '0';
|
||||
const id = '0';
|
||||
const query = "'kalle, anka','donald, duck'";
|
||||
const auto = false;
|
||||
const { initialState } = getVariableTestContext<IntervalVariableModel>(adapter, { uuid, query, auto });
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'interval' });
|
||||
const { initialState } = getVariableTestContext<IntervalVariableModel>(adapter, { id, query, auto });
|
||||
const payload = toVariablePayload({ id: '0', type: 'interval' });
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(intervalVariableReducer, cloneDeep(initialState))
|
||||
@@ -108,7 +108,7 @@ describe('intervalVariableReducer', () => {
|
||||
.thenStateShouldEqual({
|
||||
'0': {
|
||||
...initialState['0'],
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
query: "'kalle, anka','donald, duck'",
|
||||
auto: false,
|
||||
options: [
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { IntervalVariableModel, VariableHide, VariableOption, VariableRefresh } from '../../templating/variable';
|
||||
import { EMPTY_UUID, getInstanceState, VariablePayload } from '../state/types';
|
||||
import { getInstanceState, NEW_VARIABLE_ID, VariablePayload } from '../state/types';
|
||||
import { initialVariablesState, VariablesState } from '../state/variablesReducer';
|
||||
import _ from 'lodash';
|
||||
|
||||
export const initialIntervalVariableModelState: IntervalVariableModel = {
|
||||
uuid: EMPTY_UUID,
|
||||
id: NEW_VARIABLE_ID,
|
||||
global: false,
|
||||
type: 'interval',
|
||||
name: '',
|
||||
@@ -28,7 +28,7 @@ export const intervalVariableSlice = createSlice({
|
||||
initialState: initialVariablesState,
|
||||
reducers: {
|
||||
createIntervalOptions: (state: VariablesState, action: PayloadAction<VariablePayload>) => {
|
||||
const instanceState = getInstanceState<IntervalVariableModel>(state, action.payload.uuid!);
|
||||
const instanceState = getInstanceState<IntervalVariableModel>(state, action.payload.id!);
|
||||
const options: VariableOption[] = _.map(instanceState.query.match(/(["'])(.*?)\1|\w+/g), text => {
|
||||
text = text.replace(/["']+/g, '');
|
||||
return { text: text.trim(), value: text.trim(), selected: false };
|
||||
|
@@ -54,7 +54,7 @@ export class OptionsPickerUnconnected extends PureComponent<Props> {
|
||||
|
||||
render() {
|
||||
const { variable, picker } = this.props;
|
||||
const showOptions = picker.uuid === variable.uuid;
|
||||
const showOptions = picker.id === variable.id;
|
||||
|
||||
return (
|
||||
<div className="variable-link-wrapper">
|
||||
|
@@ -397,7 +397,7 @@ describe('options picker actions', () => {
|
||||
function createVariable(extend?: Partial<QueryVariableModel>): QueryVariableModel {
|
||||
return {
|
||||
type: 'query',
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
global: false,
|
||||
current: createOption(''),
|
||||
options: [],
|
||||
|
@@ -56,8 +56,8 @@ export const navigateOptions = (key: NavigationKey, clearOthers: boolean): Thunk
|
||||
|
||||
export const filterOrSearchOptions = (searchQuery: string): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
const { uuid } = getState().templating.optionsPicker;
|
||||
const { query, options } = getVariable<VariableWithOptions>(uuid!, getState());
|
||||
const { id } = getState().templating.optionsPicker;
|
||||
const { query, options } = getVariable<VariableWithOptions>(id!, getState());
|
||||
dispatch(updateSearchQuery(searchQuery));
|
||||
|
||||
if (containsSearchFilter(query)) {
|
||||
@@ -70,13 +70,13 @@ export const filterOrSearchOptions = (searchQuery: string): ThunkResult<void> =>
|
||||
export const commitChangesToVariable = (): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
const picker = getState().templating.optionsPicker;
|
||||
const existing = getVariable<VariableWithMultiSupport>(picker.uuid, getState());
|
||||
const existing = getVariable<VariableWithMultiSupport>(picker.id, getState());
|
||||
const currentPayload = { option: mapToCurrent(picker) };
|
||||
const searchQueryPayload = { propName: 'queryValue', propValue: picker.queryValue };
|
||||
|
||||
dispatch(setCurrentVariableValue(toVariablePayload(existing, currentPayload)));
|
||||
dispatch(changeVariableProp(toVariablePayload(existing, searchQueryPayload)));
|
||||
const updated = getVariable<VariableWithMultiSupport>(picker.uuid, getState());
|
||||
const updated = getVariable<VariableWithMultiSupport>(picker.id, getState());
|
||||
|
||||
if (existing.current.text === updated.current.text) {
|
||||
return dispatch(hideOptions());
|
||||
@@ -90,8 +90,8 @@ export const commitChangesToVariable = (): ThunkResult<void> => {
|
||||
|
||||
export const toggleOptionByHighlight = (clearOthers: boolean): ThunkResult<void> => {
|
||||
return (dispatch, getState) => {
|
||||
const { uuid, highlightIndex } = getState().templating.optionsPicker;
|
||||
const variable = getVariable<VariableWithMultiSupport>(uuid, getState());
|
||||
const { id, highlightIndex } = getState().templating.optionsPicker;
|
||||
const variable = getVariable<VariableWithMultiSupport>(id, getState());
|
||||
const option = variable.options[highlightIndex];
|
||||
dispatch(toggleOption({ option, forceSelect: false, clearOthers }));
|
||||
};
|
||||
@@ -111,7 +111,7 @@ export const toggleAndFetchTag = (tag: VariableTag): ThunkResult<void> => {
|
||||
const fetchTagValues = (tagText: string): ThunkResult<Promise<string[]>> => {
|
||||
return async (dispatch, getState) => {
|
||||
const picker = getState().templating.optionsPicker;
|
||||
const variable = getVariable<QueryVariableModel>(picker.uuid, getState());
|
||||
const variable = getVariable<QueryVariableModel>(picker.id, getState());
|
||||
|
||||
const datasource = await getDataSourceSrv().get(variable.datasource ?? '');
|
||||
const query = variable.tagValuesQuery.replace('$tag', tagText);
|
||||
@@ -139,13 +139,13 @@ const getTimeRange = (variable: QueryVariableModel) => {
|
||||
|
||||
const searchForOptions = async (dispatch: ThunkDispatch, getState: () => StoreState, searchQuery: string) => {
|
||||
try {
|
||||
const { uuid } = getState().templating.optionsPicker;
|
||||
const existing = getVariable<VariableWithOptions>(uuid, getState());
|
||||
const { id } = getState().templating.optionsPicker;
|
||||
const existing = getVariable<VariableWithOptions>(id, getState());
|
||||
|
||||
const adapter = variableAdapters.get(existing.type);
|
||||
await adapter.updateOptions(existing, searchQuery);
|
||||
|
||||
const updated = getVariable<VariableWithOptions>(uuid, getState());
|
||||
const updated = getVariable<VariableWithOptions>(id, getState());
|
||||
dispatch(updateOptionsFromSearch(updated.options));
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
@@ -283,7 +283,7 @@ describe('optionsPickerReducer', () => {
|
||||
query,
|
||||
options: [selected, { text: 'A', value: 'A', selected: false }, { text: 'B', value: 'B', selected: false }],
|
||||
multi: false,
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
queryValue,
|
||||
} as QueryVariableModel;
|
||||
|
||||
@@ -294,7 +294,7 @@ describe('optionsPickerReducer', () => {
|
||||
...initialState,
|
||||
options: payload.options,
|
||||
queryValue,
|
||||
uuid: payload.uuid!,
|
||||
id: payload.id!,
|
||||
multi: payload.multi,
|
||||
selectedValues: [selected],
|
||||
});
|
||||
@@ -312,14 +312,14 @@ describe('optionsPickerReducer', () => {
|
||||
{ text: 'B', value: 'B', selected: false },
|
||||
];
|
||||
const { initialState } = getVariableTestContext({});
|
||||
const payload = { type: 'query', uuid: '0', current, query, options, queryValue } as QueryVariableModel;
|
||||
const payload = { type: 'query', id: '0', current, query, options, queryValue } as QueryVariableModel;
|
||||
|
||||
reducerTester<OptionsPickerState>()
|
||||
.givenReducer(optionsPickerReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(showOptions(payload))
|
||||
.thenStateShouldEqual({
|
||||
...initialState,
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
queryValue: '',
|
||||
selectedValues: [
|
||||
{
|
||||
@@ -343,7 +343,7 @@ describe('optionsPickerReducer', () => {
|
||||
],
|
||||
queryValue: 'a search',
|
||||
highlightIndex: 1,
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
});
|
||||
|
||||
reducerTester<OptionsPickerState>()
|
||||
|
@@ -17,7 +17,7 @@ export interface ToggleOption {
|
||||
}
|
||||
|
||||
export interface OptionsPickerState {
|
||||
uuid: string;
|
||||
id: string;
|
||||
selectedValues: VariableOption[];
|
||||
selectedTags: VariableTag[];
|
||||
queryValue: string | null;
|
||||
@@ -28,7 +28,7 @@ export interface OptionsPickerState {
|
||||
}
|
||||
|
||||
export const initialState: OptionsPickerState = {
|
||||
uuid: '',
|
||||
id: '',
|
||||
highlightIndex: -1,
|
||||
queryValue: null,
|
||||
selectedTags: [],
|
||||
@@ -76,7 +76,7 @@ const optionsPickerSlice = createSlice({
|
||||
state.options = cloneDeep(options);
|
||||
state.tags = getTags(action.payload);
|
||||
state.multi = multi ?? false;
|
||||
state.uuid = action.payload.uuid!;
|
||||
state.id = action.payload.id!;
|
||||
state.queryValue = '';
|
||||
|
||||
if (isQuery(action.payload)) {
|
||||
|
@@ -163,7 +163,7 @@ describe('query actions', () => {
|
||||
const tester = await reduxTester<{ templating: TemplatingState }>()
|
||||
.givenRootReducer(getTemplatingRootReducer())
|
||||
.whenActionIsDispatched(initDashboardTemplating([variable]))
|
||||
.whenActionIsDispatched(setIdInEditor({ id: variable.uuid! }))
|
||||
.whenActionIsDispatched(setIdInEditor({ id: variable.id! }))
|
||||
.whenAsyncActionIsDispatched(updateQueryVariableOptions(toVariablePayload(variable)), true);
|
||||
|
||||
const option = createOption(ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE);
|
||||
@@ -190,7 +190,7 @@ describe('query actions', () => {
|
||||
const tester = await reduxTester<{ templating: TemplatingState }>()
|
||||
.givenRootReducer(getTemplatingRootReducer())
|
||||
.whenActionIsDispatched(initDashboardTemplating([variable]))
|
||||
.whenActionIsDispatched(setIdInEditor({ id: variable.uuid! }))
|
||||
.whenActionIsDispatched(setIdInEditor({ id: variable.id! }))
|
||||
.whenAsyncActionIsDispatched(updateQueryVariableOptions(toVariablePayload(variable)), true);
|
||||
|
||||
tester.thenDispatchedActionsPredicateShouldEqual(actions => {
|
||||
@@ -530,7 +530,7 @@ function mockDatasourceMetrics(variable: QueryVariableModel, optionsMetrics: any
|
||||
function createVariable(extend?: Partial<QueryVariableModel>): QueryVariableModel {
|
||||
return {
|
||||
type: 'query',
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
global: false,
|
||||
current: createOption(''),
|
||||
options: [],
|
||||
|
@@ -20,9 +20,9 @@ export const updateQueryVariableOptions = (
|
||||
searchFilter?: string
|
||||
): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
const variableInState = getVariable<QueryVariableModel>(identifier.uuid!, getState());
|
||||
const variableInState = getVariable<QueryVariableModel>(identifier.id!, getState());
|
||||
try {
|
||||
if (getState().templating.editor.id === variableInState.uuid) {
|
||||
if (getState().templating.editor.id === variableInState.id) {
|
||||
dispatch(removeVariableEditorError({ errorProp: 'update' }));
|
||||
}
|
||||
const dataSource = await getDatasourceSrv().get(variableInState.datasource ?? '');
|
||||
@@ -49,7 +49,7 @@ export const updateQueryVariableOptions = (
|
||||
if (err.data && err.data.message) {
|
||||
err.message = err.data.message;
|
||||
}
|
||||
if (getState().templating.editor.id === variableInState.uuid) {
|
||||
if (getState().templating.editor.id === variableInState.id) {
|
||||
dispatch(addVariableEditorError({ errorProp: 'update', errorText: err.message }));
|
||||
}
|
||||
appEvents.emit(AppEvents.alertError, [
|
||||
@@ -72,7 +72,7 @@ export const initQueryVariableEditor = (identifier: VariableIdentifier): ThunkRe
|
||||
const allDataSources = [defaultDatasource].concat(dataSources);
|
||||
dispatch(changeVariableEditorExtended({ propName: 'dataSources', propValue: allDataSources }));
|
||||
|
||||
const variable = getVariable<QueryVariableModel>(identifier.uuid!, getState());
|
||||
const variable = getVariable<QueryVariableModel>(identifier.id!, getState());
|
||||
if (!variable.datasource) {
|
||||
return;
|
||||
}
|
||||
@@ -101,7 +101,7 @@ export const changeQueryVariableQuery = (
|
||||
query: any,
|
||||
definition: string
|
||||
): ThunkResult<void> => async (dispatch, getState) => {
|
||||
const variableInState = getVariable<QueryVariableModel>(identifier.uuid!, getState());
|
||||
const variableInState = getVariable<QueryVariableModel>(identifier.id!, getState());
|
||||
if (typeof query === 'string' && query.match(new RegExp('\\$' + variableInState.name + '(/| |$)'))) {
|
||||
const errorText = 'Query cannot contain a reference to itself. Variable: $' + variableInState.name;
|
||||
dispatch(addVariableEditorError({ errorProp: 'query', errorText }));
|
||||
|
@@ -31,7 +31,7 @@ export const createQueryVariableAdapter = (): VariableAdapter<QueryVariableModel
|
||||
await dispatch(updateQueryVariableOptions(toVariableIdentifier(variable), searchFilter));
|
||||
},
|
||||
getSaveModel: variable => {
|
||||
const { index, uuid, initLock, global, queryValue, ...rest } = cloneDeep(variable);
|
||||
const { index, id, initLock, global, queryValue, ...rest } = cloneDeep(variable);
|
||||
// remove options
|
||||
if (variable.refresh !== VariableRefresh.never) {
|
||||
return { ...rest, options: [] };
|
||||
|
@@ -17,7 +17,7 @@ describe('queryVariableReducer', () => {
|
||||
{ text: 'A', value: 'A', selected: false },
|
||||
{ text: 'B', value: 'B', selected: false },
|
||||
];
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'query' }, options);
|
||||
const payload = toVariablePayload({ id: '0', type: 'query' }, options);
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(queryVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(updateVariableOptions(payload))
|
||||
@@ -42,7 +42,7 @@ describe('queryVariableReducer', () => {
|
||||
{ text: 'A', value: 'A', selected: false },
|
||||
{ text: 'B', value: 'B', selected: false },
|
||||
];
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'query' }, options);
|
||||
const payload = toVariablePayload({ id: '0', type: 'query' }, options);
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(queryVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(updateVariableOptions(payload))
|
||||
@@ -62,7 +62,7 @@ describe('queryVariableReducer', () => {
|
||||
describe('when updateVariableOptions is dispatched and includeAll is true and payload is an empty array', () => {
|
||||
it('then state should be correct', () => {
|
||||
const { initialState } = getVariableTestContext(adapter, { includeAll: true });
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'query' }, []);
|
||||
const payload = toVariablePayload({ id: '0', type: 'query' }, []);
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(queryVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(updateVariableOptions(payload))
|
||||
@@ -79,7 +79,7 @@ describe('queryVariableReducer', () => {
|
||||
describe('when updateVariableOptions is dispatched and includeAll is false and payload is an empty array', () => {
|
||||
it('then state should be correct', () => {
|
||||
const { initialState } = getVariableTestContext(adapter, { includeAll: false });
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'query' }, []);
|
||||
const payload = toVariablePayload({ id: '0', type: 'query' }, []);
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(queryVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(updateVariableOptions(payload))
|
||||
@@ -100,7 +100,7 @@ describe('queryVariableReducer', () => {
|
||||
{ text: 'A', value: 'A', selected: false },
|
||||
{ text: 'B', value: 'B', selected: false },
|
||||
];
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'query' }, options);
|
||||
const payload = toVariablePayload({ id: '0', type: 'query' }, options);
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(queryVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(updateVariableOptions(payload))
|
||||
@@ -124,7 +124,7 @@ describe('queryVariableReducer', () => {
|
||||
{ text: 'A', value: 'A', selected: false },
|
||||
{ text: 'B', value: 'B', selected: false },
|
||||
];
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'query' }, options);
|
||||
const payload = toVariablePayload({ id: '0', type: 'query' }, options);
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(queryVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(updateVariableOptions(payload))
|
||||
@@ -142,7 +142,7 @@ describe('queryVariableReducer', () => {
|
||||
it('then state should be correct', () => {
|
||||
const { initialState } = getVariableTestContext(adapter);
|
||||
const tags: any[] = [{ text: 'A' }, { text: 'B' }];
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'query' }, tags);
|
||||
const payload = toVariablePayload({ id: '0', type: 'query' }, tags);
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(queryVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(updateVariableTags(payload))
|
||||
|
@@ -14,8 +14,8 @@ import templateSrv from '../../templating/template_srv';
|
||||
import {
|
||||
ALL_VARIABLE_TEXT,
|
||||
ALL_VARIABLE_VALUE,
|
||||
EMPTY_UUID,
|
||||
getInstanceState,
|
||||
NEW_VARIABLE_ID,
|
||||
NONE_VARIABLE_TEXT,
|
||||
NONE_VARIABLE_VALUE,
|
||||
VariablePayload,
|
||||
@@ -31,7 +31,7 @@ export interface QueryVariableEditorState {
|
||||
}
|
||||
|
||||
export const initialQueryVariableModelState: QueryVariableModel = {
|
||||
uuid: EMPTY_UUID,
|
||||
id: NEW_VARIABLE_ID,
|
||||
global: false,
|
||||
index: -1,
|
||||
type: 'query',
|
||||
@@ -134,7 +134,7 @@ export const queryVariableSlice = createSlice({
|
||||
reducers: {
|
||||
updateVariableOptions: (state: VariablesState, action: PayloadAction<VariablePayload<any[]>>) => {
|
||||
const results = action.payload.data;
|
||||
const instanceState = getInstanceState<QueryVariableModel>(state, action.payload.uuid);
|
||||
const instanceState = getInstanceState<QueryVariableModel>(state, action.payload.id);
|
||||
const { regex, includeAll, sort } = instanceState;
|
||||
const options = metricNamesToVariableValues(regex, sort, results);
|
||||
|
||||
@@ -148,7 +148,7 @@ export const queryVariableSlice = createSlice({
|
||||
instanceState.options = options;
|
||||
},
|
||||
updateVariableTags: (state: VariablesState, action: PayloadAction<VariablePayload<any[]>>) => {
|
||||
const instanceState = getInstanceState<QueryVariableModel>(state, action.payload.uuid);
|
||||
const instanceState = getInstanceState<QueryVariableModel>(state, action.payload.id);
|
||||
const results = action.payload.data;
|
||||
const tags: VariableTag[] = [];
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
|
@@ -11,10 +11,10 @@ import { initialCustomVariableModelState } from '../../custom/reducer';
|
||||
import { MultiVariableBuilder } from './multiVariableBuilder';
|
||||
import { initialConstantVariableModelState } from '../../constant/reducer';
|
||||
|
||||
export const adHoc = () => new AdHocVariableBuilder(initialAdHocVariableModelState);
|
||||
export const interval = () => new IntervalVariableBuilder(initialIntervalVariableModelState);
|
||||
export const datasource = () => new DatasourceVariableBuilder(initialDataSourceVariableModelState);
|
||||
export const query = () => new DatasourceVariableBuilder(initialQueryVariableModelState);
|
||||
export const textbox = () => new OptionsVariableBuilder(initialTextBoxVariableModelState);
|
||||
export const custom = () => new MultiVariableBuilder(initialCustomVariableModelState);
|
||||
export const constant = () => new OptionsVariableBuilder(initialConstantVariableModelState);
|
||||
export const adHocBuilder = () => new AdHocVariableBuilder(initialAdHocVariableModelState);
|
||||
export const intervalBuilder = () => new IntervalVariableBuilder(initialIntervalVariableModelState);
|
||||
export const datasourceBuilder = () => new DatasourceVariableBuilder(initialDataSourceVariableModelState);
|
||||
export const queryBuilder = () => new DatasourceVariableBuilder(initialQueryVariableModelState);
|
||||
export const textboxBuilder = () => new OptionsVariableBuilder(initialTextBoxVariableModelState);
|
||||
export const customBuilder = () => new MultiVariableBuilder(initialCustomVariableModelState);
|
||||
export const constantBuilder = () => new OptionsVariableBuilder(initialConstantVariableModelState);
|
||||
|
@@ -5,7 +5,7 @@ export class VariableBuilder<T extends VariableModel> {
|
||||
protected variable: T;
|
||||
|
||||
constructor(initialState: T) {
|
||||
const { uuid, index, global, ...rest } = initialState;
|
||||
const { id, index, global, ...rest } = initialState;
|
||||
this.variable = cloneDeep({ ...rest, name: rest.type }) as T;
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ export class VariableBuilder<T extends VariableModel> {
|
||||
return this;
|
||||
}
|
||||
|
||||
withUUID(uuid: string) {
|
||||
this.variable.uuid = uuid;
|
||||
withId(id: string) {
|
||||
this.variable.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { AnyAction } from 'redux';
|
||||
import { UrlQueryMap } from '@grafana/runtime';
|
||||
import { dateTime, TimeRange } from '@grafana/data';
|
||||
|
||||
import { getTemplatingAndLocationRootReducer, getTemplatingRootReducer } from './helpers';
|
||||
import { variableAdapters } from '../adapters';
|
||||
@@ -7,6 +8,7 @@ import { createQueryVariableAdapter } from '../query/adapter';
|
||||
import { createCustomVariableAdapter } from '../custom/adapter';
|
||||
import { createTextBoxVariableAdapter } from '../textbox/adapter';
|
||||
import { createConstantVariableAdapter } from '../constant/adapter';
|
||||
import { createIntervalVariableAdapter } from '../interval/adapter';
|
||||
import { reduxTester } from '../../../../test/core/redux/reduxTester';
|
||||
import { TemplatingState } from 'app/features/variables/state/reducers';
|
||||
import {
|
||||
@@ -17,16 +19,30 @@ import {
|
||||
setOptionFromUrl,
|
||||
validateVariableSelectionState,
|
||||
} from './actions';
|
||||
import { addInitLock, addVariable, removeInitLock, resolveInitLock, setCurrentVariableValue } from './sharedReducer';
|
||||
import { toVariableIdentifier, toVariablePayload } from './types';
|
||||
import {
|
||||
addInitLock,
|
||||
addVariable,
|
||||
removeInitLock,
|
||||
removeVariable,
|
||||
resolveInitLock,
|
||||
setCurrentVariableValue,
|
||||
} from './sharedReducer';
|
||||
import { NEW_VARIABLE_ID, toVariableIdentifier, toVariablePayload } from './types';
|
||||
import { changeVariableName } from '../editor/actions';
|
||||
import { changeVariableNameFailed, changeVariableNameSucceeded, setIdInEditor } from '../editor/reducer';
|
||||
import { TemplateSrv } from '../../templating/template_srv';
|
||||
import { Emitter } from '../../../core/core';
|
||||
import { createIntervalVariableAdapter } from '../interval/adapter';
|
||||
import { VariableRefresh } from '../../templating/variable';
|
||||
import { DashboardModel } from '../../dashboard/state';
|
||||
import { DashboardState } from '../../../types';
|
||||
import { dateTime, TimeRange } from '@grafana/data';
|
||||
import * as variableBuilder from '../shared/testing/builders';
|
||||
import {
|
||||
constantBuilder,
|
||||
customBuilder,
|
||||
datasourceBuilder,
|
||||
intervalBuilder,
|
||||
queryBuilder,
|
||||
textboxBuilder,
|
||||
} from '../shared/testing/builders';
|
||||
|
||||
describe('shared actions', () => {
|
||||
describe('when initDashboardTemplating is dispatched', () => {
|
||||
@@ -35,11 +51,11 @@ describe('shared actions', () => {
|
||||
variableAdapters.set('custom', createCustomVariableAdapter());
|
||||
variableAdapters.set('textbox', createTextBoxVariableAdapter());
|
||||
variableAdapters.set('constant', createConstantVariableAdapter());
|
||||
const query = variableBuilder.query().build();
|
||||
const constant = variableBuilder.constant().build();
|
||||
const datasource = variableBuilder.datasource().build();
|
||||
const custom = variableBuilder.custom().build();
|
||||
const textbox = variableBuilder.textbox().build();
|
||||
const query = queryBuilder().build();
|
||||
const constant = constantBuilder().build();
|
||||
const datasource = datasourceBuilder().build();
|
||||
const custom = customBuilder().build();
|
||||
const textbox = textboxBuilder().build();
|
||||
const list = [query, constant, datasource, custom, textbox];
|
||||
|
||||
reduxTester<{ templating: TemplatingState }>()
|
||||
@@ -63,16 +79,16 @@ describe('shared actions', () => {
|
||||
// because uuid are dynamic we need to get the uuid from the resulting state
|
||||
// an alternative would be to add our own uuids in the model above instead
|
||||
expect(dispatchedActions[4]).toEqual(
|
||||
addInitLock(toVariablePayload({ ...query, uuid: dispatchedActions[4].payload.uuid }))
|
||||
addInitLock(toVariablePayload({ ...query, id: dispatchedActions[4].payload.id }))
|
||||
);
|
||||
expect(dispatchedActions[5]).toEqual(
|
||||
addInitLock(toVariablePayload({ ...constant, uuid: dispatchedActions[5].payload.uuid }))
|
||||
addInitLock(toVariablePayload({ ...constant, id: dispatchedActions[5].payload.id }))
|
||||
);
|
||||
expect(dispatchedActions[6]).toEqual(
|
||||
addInitLock(toVariablePayload({ ...custom, uuid: dispatchedActions[6].payload.uuid }))
|
||||
addInitLock(toVariablePayload({ ...custom, id: dispatchedActions[6].payload.id }))
|
||||
);
|
||||
expect(dispatchedActions[7]).toEqual(
|
||||
addInitLock(toVariablePayload({ ...textbox, uuid: dispatchedActions[7].payload.uuid }))
|
||||
addInitLock(toVariablePayload({ ...textbox, id: dispatchedActions[7].payload.id }))
|
||||
);
|
||||
|
||||
return true;
|
||||
@@ -86,11 +102,11 @@ describe('shared actions', () => {
|
||||
variableAdapters.set('custom', createCustomVariableAdapter());
|
||||
variableAdapters.set('textbox', createTextBoxVariableAdapter());
|
||||
variableAdapters.set('constant', createConstantVariableAdapter());
|
||||
const query = variableBuilder.query().build();
|
||||
const constant = variableBuilder.constant().build();
|
||||
const datasource = variableBuilder.datasource().build();
|
||||
const custom = variableBuilder.custom().build();
|
||||
const textbox = variableBuilder.textbox().build();
|
||||
const query = queryBuilder().build();
|
||||
const constant = constantBuilder().build();
|
||||
const datasource = datasourceBuilder().build();
|
||||
const custom = customBuilder().build();
|
||||
const textbox = textboxBuilder().build();
|
||||
const list = [query, constant, datasource, custom, textbox];
|
||||
|
||||
const tester = await reduxTester<{ templating: TemplatingState; location: { query: UrlQueryMap } }>({
|
||||
@@ -104,29 +120,29 @@ describe('shared actions', () => {
|
||||
expect(dispatchedActions.length).toEqual(8);
|
||||
|
||||
expect(dispatchedActions[0]).toEqual(
|
||||
resolveInitLock(toVariablePayload({ ...query, uuid: dispatchedActions[0].payload.uuid }))
|
||||
resolveInitLock(toVariablePayload({ ...query, id: dispatchedActions[0].payload.id }))
|
||||
);
|
||||
expect(dispatchedActions[1]).toEqual(
|
||||
resolveInitLock(toVariablePayload({ ...constant, uuid: dispatchedActions[1].payload.uuid }))
|
||||
resolveInitLock(toVariablePayload({ ...constant, id: dispatchedActions[1].payload.id }))
|
||||
);
|
||||
expect(dispatchedActions[2]).toEqual(
|
||||
resolveInitLock(toVariablePayload({ ...custom, uuid: dispatchedActions[2].payload.uuid }))
|
||||
resolveInitLock(toVariablePayload({ ...custom, id: dispatchedActions[2].payload.id }))
|
||||
);
|
||||
expect(dispatchedActions[3]).toEqual(
|
||||
resolveInitLock(toVariablePayload({ ...textbox, uuid: dispatchedActions[3].payload.uuid }))
|
||||
resolveInitLock(toVariablePayload({ ...textbox, id: dispatchedActions[3].payload.id }))
|
||||
);
|
||||
|
||||
expect(dispatchedActions[4]).toEqual(
|
||||
removeInitLock(toVariablePayload({ ...query, uuid: dispatchedActions[4].payload.uuid }))
|
||||
removeInitLock(toVariablePayload({ ...query, id: dispatchedActions[4].payload.id }))
|
||||
);
|
||||
expect(dispatchedActions[5]).toEqual(
|
||||
removeInitLock(toVariablePayload({ ...constant, uuid: dispatchedActions[5].payload.uuid }))
|
||||
removeInitLock(toVariablePayload({ ...constant, id: dispatchedActions[5].payload.id }))
|
||||
);
|
||||
expect(dispatchedActions[6]).toEqual(
|
||||
removeInitLock(toVariablePayload({ ...custom, uuid: dispatchedActions[6].payload.uuid }))
|
||||
removeInitLock(toVariablePayload({ ...custom, id: dispatchedActions[6].payload.id }))
|
||||
);
|
||||
expect(dispatchedActions[7]).toEqual(
|
||||
removeInitLock(toVariablePayload({ ...textbox, uuid: dispatchedActions[7].payload.uuid }))
|
||||
removeInitLock(toVariablePayload({ ...textbox, id: dispatchedActions[7].payload.id }))
|
||||
);
|
||||
|
||||
return true;
|
||||
@@ -146,9 +162,8 @@ describe('shared actions', () => {
|
||||
${undefined} | ${[undefined]}
|
||||
`('and urlValue is $urlValue then correct actions are dispatched', async ({ urlValue, expected }) => {
|
||||
variableAdapters.set('custom', createCustomVariableAdapter());
|
||||
const custom = variableBuilder
|
||||
.custom()
|
||||
.withUUID('0')
|
||||
const custom = customBuilder()
|
||||
.withId('0')
|
||||
.withOptions('A', 'B', 'C')
|
||||
.withCurrent('A')
|
||||
.build();
|
||||
@@ -161,7 +176,7 @@ describe('shared actions', () => {
|
||||
await tester.thenDispatchedActionsShouldEqual(
|
||||
setCurrentVariableValue(
|
||||
toVariablePayload(
|
||||
{ type: 'custom', uuid: '0' },
|
||||
{ type: 'custom', id: '0' },
|
||||
{ option: { text: expected, value: expected, selected: false } }
|
||||
)
|
||||
)
|
||||
@@ -184,16 +199,14 @@ describe('shared actions', () => {
|
||||
let custom;
|
||||
|
||||
if (!withOptions) {
|
||||
custom = variableBuilder
|
||||
.custom()
|
||||
.withUUID('0')
|
||||
custom = customBuilder()
|
||||
.withId('0')
|
||||
.withCurrent(withCurrent)
|
||||
.withoutOptions()
|
||||
.build();
|
||||
} else {
|
||||
custom = variableBuilder
|
||||
.custom()
|
||||
.withUUID('0')
|
||||
custom = customBuilder()
|
||||
.withId('0')
|
||||
.withOptions(...withOptions)
|
||||
.withCurrent(withCurrent)
|
||||
.build();
|
||||
@@ -213,7 +226,7 @@ describe('shared actions', () => {
|
||||
: [
|
||||
setCurrentVariableValue(
|
||||
toVariablePayload(
|
||||
{ type: 'custom', uuid: '0' },
|
||||
{ type: 'custom', id: '0' },
|
||||
{ option: { text: expected, value: expected, selected: false } }
|
||||
)
|
||||
),
|
||||
@@ -240,17 +253,15 @@ describe('shared actions', () => {
|
||||
let custom;
|
||||
|
||||
if (!withOptions) {
|
||||
custom = variableBuilder
|
||||
.custom()
|
||||
.withUUID('0')
|
||||
custom = customBuilder()
|
||||
.withId('0')
|
||||
.withMulti()
|
||||
.withCurrent(withCurrent)
|
||||
.withoutOptions()
|
||||
.build();
|
||||
} else {
|
||||
custom = variableBuilder
|
||||
.custom()
|
||||
.withUUID('0')
|
||||
custom = customBuilder()
|
||||
.withId('0')
|
||||
.withMulti()
|
||||
.withOptions(...withOptions)
|
||||
.withCurrent(withCurrent)
|
||||
@@ -271,7 +282,7 @@ describe('shared actions', () => {
|
||||
: [
|
||||
setCurrentVariableValue(
|
||||
toVariablePayload(
|
||||
{ type: 'custom', uuid: '0' },
|
||||
{ type: 'custom', id: '0' },
|
||||
{ option: { text: expectedText, value: expectedText, selected: expectedSelected } }
|
||||
)
|
||||
),
|
||||
@@ -316,9 +327,8 @@ describe('shared actions', () => {
|
||||
variableAdapters.set('constant', createConstantVariableAdapter());
|
||||
|
||||
// initial variable state
|
||||
const initialVariable = variableBuilder
|
||||
.interval()
|
||||
.withUUID('0')
|
||||
const initialVariable = intervalBuilder()
|
||||
.withId('interval-0')
|
||||
.withName('interval-0')
|
||||
.withOptions('1m', '10m', '30m', '1h', '6h', '12h', '1d', '7d', '14d', '30d')
|
||||
.withCurrent('1m')
|
||||
@@ -326,22 +336,20 @@ describe('shared actions', () => {
|
||||
.build();
|
||||
|
||||
// the constant variable should be filtered out
|
||||
const constant = variableBuilder
|
||||
.constant()
|
||||
.withUUID('1')
|
||||
const constant = constantBuilder()
|
||||
.withId('constant-1')
|
||||
.withName('constant-1')
|
||||
.withOptions('a constant')
|
||||
.withCurrent('a constant')
|
||||
.build();
|
||||
const initialState = {
|
||||
templating: { variables: { '0': { ...initialVariable }, '1': { ...constant } } },
|
||||
templating: { variables: { 'interval-0': { ...initialVariable }, 'constant-1': { ...constant } } },
|
||||
dashboard,
|
||||
};
|
||||
|
||||
// updated variable state
|
||||
const updatedVariable = variableBuilder
|
||||
.interval()
|
||||
.withUUID('0')
|
||||
const updatedVariable = intervalBuilder()
|
||||
.withId('interval-0')
|
||||
.withName('interval-0')
|
||||
.withOptions('1m')
|
||||
.withCurrent('1m')
|
||||
@@ -349,7 +357,7 @@ describe('shared actions', () => {
|
||||
.build();
|
||||
|
||||
const variable = args.update ? { ...updatedVariable } : { ...initialVariable };
|
||||
const state = { templating: { variables: { '0': variable, '1': { ...constant } } }, dashboard };
|
||||
const state = { templating: { variables: { 'interval-0': variable, 'constant-1': { ...constant } } }, dashboard };
|
||||
const getStateMock = jest
|
||||
.fn()
|
||||
.mockReturnValueOnce(initialState)
|
||||
@@ -443,4 +451,154 @@ describe('shared actions', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when changeVariableName is dispatched with the same name', () => {
|
||||
it('then no actions are dispatched', () => {
|
||||
const textbox = textboxBuilder()
|
||||
.withId('textbox')
|
||||
.withName('textbox')
|
||||
.build();
|
||||
const constant = constantBuilder()
|
||||
.withId('constant')
|
||||
.withName('constant')
|
||||
.build();
|
||||
|
||||
reduxTester<{ templating: TemplatingState }>()
|
||||
.givenRootReducer(getTemplatingRootReducer())
|
||||
.whenActionIsDispatched(addVariable(toVariablePayload(textbox, { global: false, index: 0, model: textbox })))
|
||||
.whenActionIsDispatched(addVariable(toVariablePayload(constant, { global: false, index: 1, model: constant })))
|
||||
.whenActionIsDispatched(changeVariableName(toVariableIdentifier(constant), constant.name), true)
|
||||
.thenNoActionsWhereDispatched();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when changeVariableName is dispatched with an unique name', () => {
|
||||
it('then the correct actions are dispatched', () => {
|
||||
const textbox = textboxBuilder()
|
||||
.withId('textbox')
|
||||
.withName('textbox')
|
||||
.build();
|
||||
const constant = constantBuilder()
|
||||
.withId('constant')
|
||||
.withName('constant')
|
||||
.build();
|
||||
|
||||
reduxTester<{ templating: TemplatingState }>()
|
||||
.givenRootReducer(getTemplatingRootReducer())
|
||||
.whenActionIsDispatched(addVariable(toVariablePayload(textbox, { global: false, index: 0, model: textbox })))
|
||||
.whenActionIsDispatched(addVariable(toVariablePayload(constant, { global: false, index: 1, model: constant })))
|
||||
.whenActionIsDispatched(changeVariableName(toVariableIdentifier(constant), 'constant1'), true)
|
||||
.thenDispatchedActionsShouldEqual(
|
||||
addVariable({
|
||||
type: 'constant',
|
||||
id: 'constant1',
|
||||
data: {
|
||||
global: false,
|
||||
index: 1,
|
||||
model: { ...constant, name: 'constant1', id: 'constant1', global: false, index: 1 },
|
||||
},
|
||||
}),
|
||||
changeVariableNameSucceeded({ type: 'constant', id: 'constant1', data: { newName: 'constant1' } }),
|
||||
setIdInEditor({ id: 'constant1' }),
|
||||
removeVariable({ type: 'constant', id: 'constant', data: { reIndex: false } })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when changeVariableName is dispatched with an unique name for a new variable', () => {
|
||||
it('then the correct actions are dispatched', () => {
|
||||
const textbox = textboxBuilder()
|
||||
.withId('textbox')
|
||||
.withName('textbox')
|
||||
.build();
|
||||
const constant = constantBuilder()
|
||||
.withId(NEW_VARIABLE_ID)
|
||||
.withName('constant')
|
||||
.build();
|
||||
|
||||
reduxTester<{ templating: TemplatingState }>()
|
||||
.givenRootReducer(getTemplatingRootReducer())
|
||||
.whenActionIsDispatched(addVariable(toVariablePayload(textbox, { global: false, index: 0, model: textbox })))
|
||||
.whenActionIsDispatched(addVariable(toVariablePayload(constant, { global: false, index: 1, model: constant })))
|
||||
.whenActionIsDispatched(changeVariableName(toVariableIdentifier(constant), 'constant1'), true)
|
||||
.thenDispatchedActionsShouldEqual(
|
||||
changeVariableNameSucceeded({ type: 'constant', id: NEW_VARIABLE_ID, data: { newName: 'constant1' } })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when changeVariableName is dispatched with __newName', () => {
|
||||
it('then the correct actions are dispatched', () => {
|
||||
const textbox = textboxBuilder()
|
||||
.withId('textbox')
|
||||
.withName('textbox')
|
||||
.build();
|
||||
const constant = constantBuilder()
|
||||
.withId('constant')
|
||||
.withName('constant')
|
||||
.build();
|
||||
|
||||
reduxTester<{ templating: TemplatingState }>()
|
||||
.givenRootReducer(getTemplatingRootReducer())
|
||||
.whenActionIsDispatched(addVariable(toVariablePayload(textbox, { global: false, index: 0, model: textbox })))
|
||||
.whenActionIsDispatched(addVariable(toVariablePayload(constant, { global: false, index: 1, model: constant })))
|
||||
.whenActionIsDispatched(changeVariableName(toVariableIdentifier(constant), '__newName'), true)
|
||||
.thenDispatchedActionsShouldEqual(
|
||||
changeVariableNameFailed({
|
||||
newName: '__newName',
|
||||
errorText: "Template names cannot begin with '__', that's reserved for Grafana's global variables",
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when changeVariableName is dispatched with illegal characters', () => {
|
||||
it('then the correct actions are dispatched', () => {
|
||||
const textbox = textboxBuilder()
|
||||
.withId('textbox')
|
||||
.withName('textbox')
|
||||
.build();
|
||||
const constant = constantBuilder()
|
||||
.withId('constant')
|
||||
.withName('constant')
|
||||
.build();
|
||||
|
||||
reduxTester<{ templating: TemplatingState }>()
|
||||
.givenRootReducer(getTemplatingRootReducer())
|
||||
.whenActionIsDispatched(addVariable(toVariablePayload(textbox, { global: false, index: 0, model: textbox })))
|
||||
.whenActionIsDispatched(addVariable(toVariablePayload(constant, { global: false, index: 1, model: constant })))
|
||||
.whenActionIsDispatched(changeVariableName(toVariableIdentifier(constant), '#constant!'), true)
|
||||
.thenDispatchedActionsShouldEqual(
|
||||
changeVariableNameFailed({
|
||||
newName: '#constant!',
|
||||
errorText: 'Only word and digit characters are allowed in variable names',
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when changeVariableName is dispatched with a name that is already used', () => {
|
||||
it('then the correct actions are dispatched', () => {
|
||||
const textbox = textboxBuilder()
|
||||
.withId('textbox')
|
||||
.withName('textbox')
|
||||
.build();
|
||||
const constant = constantBuilder()
|
||||
.withId('constant')
|
||||
.withName('constant')
|
||||
.build();
|
||||
|
||||
reduxTester<{ templating: TemplatingState }>()
|
||||
.givenRootReducer(getTemplatingRootReducer())
|
||||
.whenActionIsDispatched(addVariable(toVariablePayload(textbox, { global: false, index: 0, model: textbox })))
|
||||
.whenActionIsDispatched(addVariable(toVariablePayload(constant, { global: false, index: 1, model: constant })))
|
||||
.whenActionIsDispatched(changeVariableName(toVariableIdentifier(constant), 'textbox'), true)
|
||||
.thenDispatchedActionsShouldEqual(
|
||||
changeVariableNameFailed({
|
||||
newName: 'textbox',
|
||||
errorText: 'Variable with the same name already exists',
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -88,7 +88,7 @@ export const processVariableDependencies = async (variable: VariableModel, state
|
||||
|
||||
export const processVariable = (identifier: VariableIdentifier, queryParams: UrlQueryMap): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
const variable = getVariable(identifier.uuid!, getState());
|
||||
const variable = getVariable(identifier.id!, getState());
|
||||
await processVariableDependencies(variable, getState());
|
||||
|
||||
const urlValue = queryParams['var-' + variable.name];
|
||||
@@ -131,14 +131,14 @@ export const processVariables = (): ThunkResult<void> => {
|
||||
|
||||
export const setOptionFromUrl = (identifier: VariableIdentifier, urlValue: UrlQueryValue): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
const variable = getVariable(identifier.uuid!, getState());
|
||||
const variable = getVariable(identifier.id!, getState());
|
||||
if (variable.hasOwnProperty('refresh') && (variable as QueryVariableModel).refresh !== VariableRefresh.never) {
|
||||
// updates options
|
||||
await variableAdapters.get(variable.type).updateOptions(variable);
|
||||
}
|
||||
|
||||
// get variable from state
|
||||
const variableFromState = getVariable<VariableWithOptions>(variable.uuid!, getState());
|
||||
const variableFromState = getVariable<VariableWithOptions>(variable.id!, getState());
|
||||
if (!variableFromState) {
|
||||
throw new Error(`Couldn't find variable with name: ${variable.name}`);
|
||||
}
|
||||
@@ -212,7 +212,7 @@ export const validateVariableSelectionState = (
|
||||
defaultValue?: string
|
||||
): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
const variableInState = getVariable<VariableWithOptions>(identifier.uuid!, getState());
|
||||
const variableInState = getVariable<VariableWithOptions>(identifier.id!, getState());
|
||||
const current = variableInState.current || (({} as unknown) as VariableOption);
|
||||
const setValue = variableAdapters.get(variableInState.type).setValue;
|
||||
|
||||
@@ -296,7 +296,7 @@ const createGraph = (variables: VariableModel[]) => {
|
||||
export const variableUpdated = (identifier: VariableIdentifier, emitChangeEvents: boolean): ThunkResult<void> => {
|
||||
return (dispatch, getState) => {
|
||||
// if there is a variable lock ignore cascading update because we are in a boot up scenario
|
||||
const variable = getVariable(identifier.uuid!, getState());
|
||||
const variable = getVariable(identifier.id!, getState());
|
||||
if (variable.initLock) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -349,7 +349,7 @@ export const onTimeRangeUpdated = (
|
||||
const promises = variablesThatNeedRefresh.map(async (variable: VariableWithOptions) => {
|
||||
const previousOptions = variable.options.slice();
|
||||
await variableAdapters.get(variable.type).updateOptions(variable);
|
||||
const updatedVariable = getVariable<VariableWithOptions>(variable.uuid!, getState());
|
||||
const updatedVariable = getVariable<VariableWithOptions>(variable.id!, getState());
|
||||
if (angular.toJson(previousOptions) !== angular.toJson(updatedVariable.options)) {
|
||||
const dashboard = getState().dashboard.getModel();
|
||||
dashboard?.templateVariableValueUpdated();
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { combineReducers } from '@reduxjs/toolkit';
|
||||
|
||||
import { EMPTY_UUID } from './types';
|
||||
import { NEW_VARIABLE_ID } from './types';
|
||||
import { VariableHide, VariableModel } from '../../templating/variable';
|
||||
import { variablesReducer, VariablesState } from './variablesReducer';
|
||||
import { optionsPickerReducer } from '../pickers/OptionsPicker/reducer';
|
||||
@@ -18,7 +18,7 @@ export const getVariableState = (
|
||||
|
||||
for (let index = 0; index < noOfVariables; index++) {
|
||||
variables[index] = {
|
||||
uuid: index.toString(),
|
||||
id: index.toString(),
|
||||
type: 'query',
|
||||
name: `Name-${index}`,
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -29,13 +29,13 @@ export const getVariableState = (
|
||||
}
|
||||
|
||||
if (includeEmpty) {
|
||||
variables[EMPTY_UUID] = {
|
||||
uuid: EMPTY_UUID,
|
||||
variables[NEW_VARIABLE_ID] = {
|
||||
id: NEW_VARIABLE_ID,
|
||||
type: 'query',
|
||||
name: `Name-${EMPTY_UUID}`,
|
||||
name: `Name-${NEW_VARIABLE_ID}`,
|
||||
hide: VariableHide.dontHide,
|
||||
index: noOfVariables,
|
||||
label: `Label-${EMPTY_UUID}`,
|
||||
label: `Label-${NEW_VARIABLE_ID}`,
|
||||
skipUrlSync: false,
|
||||
};
|
||||
}
|
||||
@@ -49,7 +49,7 @@ export const getVariableTestContext = <Model extends VariableModel>(
|
||||
) => {
|
||||
const defaultVariable = {
|
||||
...adapter.initialState,
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
index: 0,
|
||||
name: '0',
|
||||
};
|
||||
|
@@ -11,7 +11,9 @@ import { resolveInitLock, setCurrentVariableValue } from './sharedReducer';
|
||||
import { toVariableIdentifier, toVariablePayload } from './types';
|
||||
import { VariableRefresh } from '../../templating/variable';
|
||||
import { updateVariableOptions } from '../query/reducer';
|
||||
import * as variableBuilder from '../shared/testing/builders';
|
||||
import { customBuilder, queryBuilder } from '../shared/testing/builders';
|
||||
|
||||
'../shared/testing/builders';
|
||||
|
||||
jest.mock('app/features/dashboard/services/TimeSrv', () => ({
|
||||
getTimeSrv: jest.fn().mockReturnValue({
|
||||
@@ -68,26 +70,24 @@ describe('processVariable', () => {
|
||||
const getAndSetupProcessVariableContext = () => {
|
||||
variableAdapters.set('custom', createCustomVariableAdapter());
|
||||
variableAdapters.set('query', createQueryVariableAdapter());
|
||||
const custom = variableBuilder
|
||||
.custom()
|
||||
.withUUID('0')
|
||||
const custom = customBuilder()
|
||||
.withId('custom')
|
||||
.withName('custom')
|
||||
.withQuery('A,B,C')
|
||||
.withOptions('A', 'B', 'C')
|
||||
.withCurrent('A')
|
||||
.build();
|
||||
|
||||
const queryDependsOnCustom = variableBuilder
|
||||
.query()
|
||||
.withUUID('1')
|
||||
const queryDependsOnCustom = queryBuilder()
|
||||
.withId('queryDependsOnCustom')
|
||||
.withName('queryDependsOnCustom')
|
||||
.withQuery('$custom.*')
|
||||
.withOptions('AA', 'AB', 'AC')
|
||||
.withCurrent('AA')
|
||||
.build();
|
||||
|
||||
const queryNoDepends = variableBuilder
|
||||
.query()
|
||||
.withUUID('2')
|
||||
const queryNoDepends = queryBuilder()
|
||||
.withId('queryNoDepends')
|
||||
.withName('queryNoDepends')
|
||||
.withQuery('*')
|
||||
.withOptions('A', 'B', 'C')
|
||||
@@ -116,7 +116,7 @@ describe('processVariable', () => {
|
||||
.whenAsyncActionIsDispatched(processVariable(toVariableIdentifier(custom), queryParams), true);
|
||||
|
||||
await tester.thenDispatchedActionsShouldEqual(
|
||||
resolveInitLock(toVariablePayload({ type: 'custom', uuid: '0' }))
|
||||
resolveInitLock(toVariablePayload({ type: 'custom', id: 'custom' }))
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -132,9 +132,12 @@ describe('processVariable', () => {
|
||||
|
||||
await tester.thenDispatchedActionsShouldEqual(
|
||||
setCurrentVariableValue(
|
||||
toVariablePayload({ type: 'custom', uuid: '0' }, { option: { text: ['B'], value: ['B'], selected: false } })
|
||||
toVariablePayload(
|
||||
{ type: 'custom', id: 'custom' },
|
||||
{ option: { text: ['B'], value: ['B'], selected: false } }
|
||||
)
|
||||
),
|
||||
resolveInitLock(toVariablePayload({ type: 'custom', uuid: '0' }))
|
||||
resolveInitLock(toVariablePayload({ type: 'custom', id: 'custom' }))
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -156,7 +159,7 @@ describe('processVariable', () => {
|
||||
.whenAsyncActionIsDispatched(processVariable(toVariableIdentifier(queryNoDepends), queryParams), true);
|
||||
|
||||
await tester.thenDispatchedActionsShouldEqual(
|
||||
resolveInitLock(toVariablePayload({ type: 'query', uuid: '2' }))
|
||||
resolveInitLock(toVariablePayload({ type: 'query', id: 'queryNoDepends' }))
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -173,16 +176,19 @@ describe('processVariable', () => {
|
||||
|
||||
await tester.thenDispatchedActionsShouldEqual(
|
||||
updateVariableOptions(
|
||||
toVariablePayload({ type: 'query', uuid: '2' }, [
|
||||
toVariablePayload({ type: 'query', id: 'queryNoDepends' }, [
|
||||
{ value: 'A', text: 'A' },
|
||||
{ value: 'B', text: 'B' },
|
||||
{ value: 'C', text: 'C' },
|
||||
])
|
||||
),
|
||||
setCurrentVariableValue(
|
||||
toVariablePayload({ type: 'query', uuid: '2' }, { option: { text: 'A', value: 'A', selected: false } })
|
||||
toVariablePayload(
|
||||
{ type: 'query', id: 'queryNoDepends' },
|
||||
{ option: { text: 'A', value: 'A', selected: false } }
|
||||
)
|
||||
),
|
||||
resolveInitLock(toVariablePayload({ type: 'query', uuid: '2' }))
|
||||
resolveInitLock(toVariablePayload({ type: 'query', id: 'queryNoDepends' }))
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -205,11 +211,11 @@ describe('processVariable', () => {
|
||||
await tester.thenDispatchedActionsShouldEqual(
|
||||
setCurrentVariableValue(
|
||||
toVariablePayload(
|
||||
{ type: 'query', uuid: '2' },
|
||||
{ type: 'query', id: 'queryNoDepends' },
|
||||
{ option: { text: ['B'], value: ['B'], selected: false } }
|
||||
)
|
||||
),
|
||||
resolveInitLock(toVariablePayload({ type: 'query', uuid: '2' }))
|
||||
resolveInitLock(toVariablePayload({ type: 'query', id: 'queryNoDepends' }))
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -230,22 +236,25 @@ describe('processVariable', () => {
|
||||
|
||||
await tester.thenDispatchedActionsShouldEqual(
|
||||
updateVariableOptions(
|
||||
toVariablePayload({ type: 'query', uuid: '2' }, [
|
||||
toVariablePayload({ type: 'query', id: 'queryNoDepends' }, [
|
||||
{ value: 'A', text: 'A' },
|
||||
{ value: 'B', text: 'B' },
|
||||
{ value: 'C', text: 'C' },
|
||||
])
|
||||
),
|
||||
setCurrentVariableValue(
|
||||
toVariablePayload({ type: 'query', uuid: '2' }, { option: { text: 'A', value: 'A', selected: false } })
|
||||
toVariablePayload(
|
||||
{ type: 'query', id: 'queryNoDepends' },
|
||||
{ option: { text: 'A', value: 'A', selected: false } }
|
||||
)
|
||||
),
|
||||
setCurrentVariableValue(
|
||||
toVariablePayload(
|
||||
{ type: 'query', uuid: '2' },
|
||||
{ type: 'query', id: 'queryNoDepends' },
|
||||
{ option: { text: ['B'], value: ['B'], selected: false } }
|
||||
)
|
||||
),
|
||||
resolveInitLock(toVariablePayload({ type: 'query', uuid: '2' }))
|
||||
resolveInitLock(toVariablePayload({ type: 'query', id: 'queryNoDepends' }))
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -274,7 +283,7 @@ describe('processVariable', () => {
|
||||
);
|
||||
|
||||
await tester.thenDispatchedActionsShouldEqual(
|
||||
resolveInitLock(toVariablePayload({ type: 'query', uuid: '1' }))
|
||||
resolveInitLock(toVariablePayload({ type: 'query', id: 'queryDependsOnCustom' }))
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -296,7 +305,7 @@ describe('processVariable', () => {
|
||||
|
||||
await tester.thenDispatchedActionsShouldEqual(
|
||||
updateVariableOptions(
|
||||
toVariablePayload({ type: 'query', uuid: '1' }, [
|
||||
toVariablePayload({ type: 'query', id: 'queryDependsOnCustom' }, [
|
||||
{ value: 'AA', text: 'AA' },
|
||||
{ value: 'AB', text: 'AB' },
|
||||
{ value: 'AC', text: 'AC' },
|
||||
@@ -304,11 +313,11 @@ describe('processVariable', () => {
|
||||
),
|
||||
setCurrentVariableValue(
|
||||
toVariablePayload(
|
||||
{ type: 'query', uuid: '1' },
|
||||
{ type: 'query', id: 'queryDependsOnCustom' },
|
||||
{ option: { text: 'AA', value: 'AA', selected: false } }
|
||||
)
|
||||
),
|
||||
resolveInitLock(toVariablePayload({ type: 'query', uuid: '1' }))
|
||||
resolveInitLock(toVariablePayload({ type: 'query', id: 'queryDependsOnCustom' }))
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -336,11 +345,11 @@ describe('processVariable', () => {
|
||||
await tester.thenDispatchedActionsShouldEqual(
|
||||
setCurrentVariableValue(
|
||||
toVariablePayload(
|
||||
{ type: 'query', uuid: '1' },
|
||||
{ type: 'query', id: 'queryDependsOnCustom' },
|
||||
{ option: { text: ['AB'], value: ['AB'], selected: false } }
|
||||
)
|
||||
),
|
||||
resolveInitLock(toVariablePayload({ type: 'query', uuid: '1' }))
|
||||
resolveInitLock(toVariablePayload({ type: 'query', id: 'queryDependsOnCustom' }))
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -366,7 +375,7 @@ describe('processVariable', () => {
|
||||
|
||||
await tester.thenDispatchedActionsShouldEqual(
|
||||
updateVariableOptions(
|
||||
toVariablePayload({ type: 'query', uuid: '1' }, [
|
||||
toVariablePayload({ type: 'query', id: 'queryDependsOnCustom' }, [
|
||||
{ value: 'AA', text: 'AA' },
|
||||
{ value: 'AB', text: 'AB' },
|
||||
{ value: 'AC', text: 'AC' },
|
||||
@@ -374,17 +383,17 @@ describe('processVariable', () => {
|
||||
),
|
||||
setCurrentVariableValue(
|
||||
toVariablePayload(
|
||||
{ type: 'query', uuid: '1' },
|
||||
{ type: 'query', id: 'queryDependsOnCustom' },
|
||||
{ option: { text: 'AA', value: 'AA', selected: false } }
|
||||
)
|
||||
),
|
||||
setCurrentVariableValue(
|
||||
toVariablePayload(
|
||||
{ type: 'query', uuid: '1' },
|
||||
{ type: 'query', id: 'queryDependsOnCustom' },
|
||||
{ option: { text: ['AB'], value: ['AB'], selected: false } }
|
||||
)
|
||||
),
|
||||
resolveInitLock(toVariablePayload({ type: 'query', uuid: '1' }))
|
||||
resolveInitLock(toVariablePayload({ type: 'query', id: 'queryDependsOnCustom' }))
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@@ -11,7 +11,7 @@ describe('variablesReducer', () => {
|
||||
it('then all variables except global variables should be removed', () => {
|
||||
const initialState: VariablesState = {
|
||||
'0': {
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
type: 'query',
|
||||
name: 'Name-0',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -20,7 +20,7 @@ describe('variablesReducer', () => {
|
||||
skipUrlSync: false,
|
||||
},
|
||||
'1': {
|
||||
uuid: '1',
|
||||
id: '1',
|
||||
type: 'query',
|
||||
name: 'Name-1',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -30,7 +30,7 @@ describe('variablesReducer', () => {
|
||||
global: true,
|
||||
},
|
||||
'2': {
|
||||
uuid: '2',
|
||||
id: '2',
|
||||
type: 'query',
|
||||
name: 'Name-2',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -39,7 +39,7 @@ describe('variablesReducer', () => {
|
||||
skipUrlSync: false,
|
||||
},
|
||||
'3': {
|
||||
uuid: '3',
|
||||
id: '3',
|
||||
type: 'query',
|
||||
name: 'Name-3',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -55,7 +55,7 @@ describe('variablesReducer', () => {
|
||||
.whenActionIsDispatched(cleanUpDashboard())
|
||||
.thenStateShouldEqual({
|
||||
'1': {
|
||||
uuid: '1',
|
||||
id: '1',
|
||||
type: 'query',
|
||||
name: 'Name-1',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -65,7 +65,7 @@ describe('variablesReducer', () => {
|
||||
global: true,
|
||||
},
|
||||
'3': {
|
||||
uuid: '3',
|
||||
id: '3',
|
||||
type: 'query',
|
||||
name: 'Name-3',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -82,7 +82,7 @@ describe('variablesReducer', () => {
|
||||
it('then the reducer for that variableAdapter should be invoked', () => {
|
||||
const initialState: VariablesState = {
|
||||
'0': {
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
type: 'query',
|
||||
name: 'Name-0',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -109,12 +109,12 @@ describe('variablesReducer', () => {
|
||||
const mockAction = createAction<VariablePayload>('mockAction');
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(variablesReducer, initialState)
|
||||
.whenActionIsDispatched(mockAction(toVariablePayload({ type: 'query', uuid: '0' })))
|
||||
.whenActionIsDispatched(mockAction(toVariablePayload({ type: 'query', id: '0' })))
|
||||
.thenStateShouldEqual(initialState);
|
||||
expect(variableAdapter.reducer).toHaveBeenCalledTimes(1);
|
||||
expect(variableAdapter.reducer).toHaveBeenCalledWith(
|
||||
initialState,
|
||||
mockAction(toVariablePayload({ type: 'query', uuid: '0' }))
|
||||
mockAction(toVariablePayload({ type: 'query', id: '0' }))
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -123,7 +123,7 @@ describe('variablesReducer', () => {
|
||||
it('then the reducer for that variableAdapter should be invoked', () => {
|
||||
const initialState: VariablesState = {
|
||||
'0': {
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
type: 'query',
|
||||
name: 'Name-0',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -150,7 +150,7 @@ describe('variablesReducer', () => {
|
||||
const mockAction = createAction<VariablePayload>('mockAction');
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(variablesReducer, initialState)
|
||||
.whenActionIsDispatched(mockAction(toVariablePayload({ type: 'adhoc', uuid: '0' })))
|
||||
.whenActionIsDispatched(mockAction(toVariablePayload({ type: 'adhoc', id: '0' })))
|
||||
.thenStateShouldEqual(initialState);
|
||||
expect(variableAdapter.reducer).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
@@ -160,7 +160,7 @@ describe('variablesReducer', () => {
|
||||
it('then the reducer for that variableAdapter should be invoked', () => {
|
||||
const initialState: VariablesState = {
|
||||
'0': {
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
type: 'query',
|
||||
name: 'Name-0',
|
||||
hide: VariableHide.dontHide,
|
||||
|
@@ -3,34 +3,38 @@ import { cloneDeep } from 'lodash';
|
||||
import { StoreState } from '../../../types';
|
||||
import { VariableModel } from '../../templating/variable';
|
||||
import { getState } from '../../../store/store';
|
||||
import { EMPTY_UUID } from './types';
|
||||
import { NEW_VARIABLE_ID } from './types';
|
||||
|
||||
export const getVariable = <T extends VariableModel = VariableModel>(
|
||||
uuid: string,
|
||||
state: StoreState = getState()
|
||||
id: string,
|
||||
state: StoreState = getState(),
|
||||
throwWhenMissing = true
|
||||
): T => {
|
||||
if (!state.templating.variables[uuid]) {
|
||||
throw new Error(`Couldn't find variable with uuid:${uuid}`);
|
||||
if (!state.templating.variables[id]) {
|
||||
if (throwWhenMissing) {
|
||||
throw new Error(`Couldn't find variable with id:${id}`);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return state.templating.variables[uuid] as T;
|
||||
return state.templating.variables[id] as T;
|
||||
};
|
||||
|
||||
export const getFilteredVariables = (filter: (model: VariableModel) => boolean, state: StoreState = getState()) => {
|
||||
return Object.values(state.templating.variables).filter(filter);
|
||||
};
|
||||
|
||||
export const getVariableWithName = (name: string, state: StoreState = getState()) => {
|
||||
return Object.values(state.templating.variables).find(variable => variable.name === name);
|
||||
export const getVariableWithName = (name: string) => {
|
||||
return getVariable(name, getState(), false);
|
||||
};
|
||||
|
||||
export const getVariables = (state: StoreState = getState()): VariableModel[] => {
|
||||
return getFilteredVariables(variable => variable.uuid! !== EMPTY_UUID, state);
|
||||
return getFilteredVariables(variable => variable.id! !== NEW_VARIABLE_ID, state);
|
||||
};
|
||||
|
||||
export const getVariableClones = (state: StoreState = getState(), includeEmptyUuid = false): VariableModel[] => {
|
||||
const variables = getFilteredVariables(
|
||||
variable => (includeEmptyUuid ? true : variable.uuid !== EMPTY_UUID),
|
||||
variable => (includeEmptyUuid ? true : variable.id! !== NEW_VARIABLE_ID),
|
||||
state
|
||||
).map(variable => cloneDeep(variable));
|
||||
return variables.sort((s1, s2) => s1.index! - s2.index!);
|
||||
|
@@ -15,19 +15,20 @@ import {
|
||||
storeNewVariable,
|
||||
} from './sharedReducer';
|
||||
import { QueryVariableModel, VariableHide } from '../../templating/variable';
|
||||
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, EMPTY_UUID, toVariablePayload } from './types';
|
||||
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, NEW_VARIABLE_ID, toVariablePayload } from './types';
|
||||
import { variableAdapters } from '../adapters';
|
||||
import { createQueryVariableAdapter } from '../query/adapter';
|
||||
import { initialQueryVariableModelState } from '../query/reducer';
|
||||
import { Deferred } from '../../../core/utils/deferred';
|
||||
import { getVariableState, getVariableTestContext } from './helpers';
|
||||
import { initialVariablesState, VariablesState } from './variablesReducer';
|
||||
import { changeVariableNameSucceeded } from '../editor/reducer';
|
||||
|
||||
describe('sharedReducer', () => {
|
||||
describe('when addVariable is dispatched', () => {
|
||||
it('then state should be correct', () => {
|
||||
const model = ({ name: 'name from model', type: 'type from model' } as unknown) as QueryVariableModel;
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'query' }, { global: true, index: 0, model });
|
||||
const payload = toVariablePayload({ id: '0', type: 'query' }, { global: true, index: 0, model });
|
||||
variableAdapters.set('query', createQueryVariableAdapter());
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(sharedReducer, { ...initialVariablesState })
|
||||
@@ -36,7 +37,7 @@ describe('sharedReducer', () => {
|
||||
[0]: {
|
||||
...initialQueryVariableModelState,
|
||||
...model,
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
global: true,
|
||||
index: 0,
|
||||
},
|
||||
@@ -47,13 +48,13 @@ describe('sharedReducer', () => {
|
||||
describe('when removeVariable is dispatched and reIndex is true', () => {
|
||||
it('then state should be correct', () => {
|
||||
const initialState: VariablesState = getVariableState(3);
|
||||
const payload = toVariablePayload({ uuid: '1', type: 'query' }, { reIndex: true });
|
||||
const payload = toVariablePayload({ id: '1', type: 'query' }, { reIndex: true });
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(sharedReducer, initialState)
|
||||
.whenActionIsDispatched(removeVariable(payload))
|
||||
.thenStateShouldEqual({
|
||||
'0': {
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
type: 'query',
|
||||
name: 'Name-0',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -62,7 +63,7 @@ describe('sharedReducer', () => {
|
||||
skipUrlSync: false,
|
||||
},
|
||||
'2': {
|
||||
uuid: '2',
|
||||
id: '2',
|
||||
type: 'query',
|
||||
name: 'Name-2',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -77,13 +78,13 @@ describe('sharedReducer', () => {
|
||||
describe('when removeVariable is dispatched and reIndex is false', () => {
|
||||
it('then state should be correct', () => {
|
||||
const initialState: VariablesState = getVariableState(3);
|
||||
const payload = toVariablePayload({ uuid: '1', type: 'query' }, { reIndex: false });
|
||||
const payload = toVariablePayload({ id: '1', type: 'query' }, { reIndex: false });
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(sharedReducer, initialState)
|
||||
.whenActionIsDispatched(removeVariable(payload))
|
||||
.thenStateShouldEqual({
|
||||
'0': {
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
type: 'query',
|
||||
name: 'Name-0',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -92,7 +93,7 @@ describe('sharedReducer', () => {
|
||||
skipUrlSync: false,
|
||||
},
|
||||
'2': {
|
||||
uuid: '2',
|
||||
id: '2',
|
||||
type: 'query',
|
||||
name: 'Name-2',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -108,13 +109,13 @@ describe('sharedReducer', () => {
|
||||
it('then state should be correct', () => {
|
||||
variableAdapters.set('query', createQueryVariableAdapter());
|
||||
const initialState: VariablesState = getVariableState(3);
|
||||
const payload = toVariablePayload({ uuid: '1', type: 'query' }, { newUuid: '11' });
|
||||
const payload = toVariablePayload({ id: '1', type: 'query' }, { newId: '11' });
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(sharedReducer, initialState)
|
||||
.whenActionIsDispatched(duplicateVariable(payload))
|
||||
.thenStateShouldEqual({
|
||||
'0': {
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
type: 'query',
|
||||
name: 'Name-0',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -123,7 +124,7 @@ describe('sharedReducer', () => {
|
||||
skipUrlSync: false,
|
||||
},
|
||||
'1': {
|
||||
uuid: '1',
|
||||
id: '1',
|
||||
type: 'query',
|
||||
name: 'Name-1',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -132,7 +133,7 @@ describe('sharedReducer', () => {
|
||||
skipUrlSync: false,
|
||||
},
|
||||
'2': {
|
||||
uuid: '2',
|
||||
id: '2',
|
||||
type: 'query',
|
||||
name: 'Name-2',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -142,7 +143,7 @@ describe('sharedReducer', () => {
|
||||
},
|
||||
'11': {
|
||||
...initialQueryVariableModelState,
|
||||
uuid: '11',
|
||||
id: '11',
|
||||
name: 'copy_of_Name-1',
|
||||
index: 3,
|
||||
label: 'Label-1',
|
||||
@@ -154,13 +155,13 @@ describe('sharedReducer', () => {
|
||||
describe('when changeVariableOrder is dispatched', () => {
|
||||
it('then state should be correct', () => {
|
||||
const initialState: VariablesState = getVariableState(3);
|
||||
const payload = toVariablePayload({ uuid: '1', type: 'query' }, { fromIndex: 1, toIndex: 0 });
|
||||
const payload = toVariablePayload({ id: '1', type: 'query' }, { fromIndex: 1, toIndex: 0 });
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(sharedReducer, initialState)
|
||||
.whenActionIsDispatched(changeVariableOrder(payload))
|
||||
.thenStateShouldEqual({
|
||||
'0': {
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
type: 'query',
|
||||
name: 'Name-0',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -169,7 +170,7 @@ describe('sharedReducer', () => {
|
||||
skipUrlSync: false,
|
||||
},
|
||||
'1': {
|
||||
uuid: '1',
|
||||
id: '1',
|
||||
type: 'query',
|
||||
name: 'Name-1',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -178,7 +179,7 @@ describe('sharedReducer', () => {
|
||||
skipUrlSync: false,
|
||||
},
|
||||
'2': {
|
||||
uuid: '2',
|
||||
id: '2',
|
||||
type: 'query',
|
||||
name: 'Name-2',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -194,13 +195,13 @@ describe('sharedReducer', () => {
|
||||
it('then state should be correct', () => {
|
||||
variableAdapters.set('query', createQueryVariableAdapter());
|
||||
const initialState: VariablesState = getVariableState(3, -1, true);
|
||||
const payload = toVariablePayload({ uuid: '11', type: 'query' });
|
||||
const payload = toVariablePayload({ id: '11', type: 'query' });
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(sharedReducer, initialState)
|
||||
.whenActionIsDispatched(storeNewVariable(payload))
|
||||
.thenStateShouldEqual({
|
||||
'0': {
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
type: 'query',
|
||||
name: 'Name-0',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -209,7 +210,7 @@ describe('sharedReducer', () => {
|
||||
skipUrlSync: false,
|
||||
},
|
||||
'1': {
|
||||
uuid: '1',
|
||||
id: '1',
|
||||
type: 'query',
|
||||
name: 'Name-1',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -218,7 +219,7 @@ describe('sharedReducer', () => {
|
||||
skipUrlSync: false,
|
||||
},
|
||||
'2': {
|
||||
uuid: '2',
|
||||
id: '2',
|
||||
type: 'query',
|
||||
name: 'Name-2',
|
||||
hide: VariableHide.dontHide,
|
||||
@@ -226,21 +227,21 @@ describe('sharedReducer', () => {
|
||||
label: 'Label-2',
|
||||
skipUrlSync: false,
|
||||
},
|
||||
[EMPTY_UUID]: {
|
||||
uuid: EMPTY_UUID,
|
||||
[NEW_VARIABLE_ID]: {
|
||||
id: NEW_VARIABLE_ID,
|
||||
type: 'query',
|
||||
name: `Name-${EMPTY_UUID}`,
|
||||
name: `Name-${NEW_VARIABLE_ID}`,
|
||||
hide: VariableHide.dontHide,
|
||||
index: 3,
|
||||
label: `Label-${EMPTY_UUID}`,
|
||||
label: `Label-${NEW_VARIABLE_ID}`,
|
||||
skipUrlSync: false,
|
||||
},
|
||||
[11]: {
|
||||
...initialQueryVariableModelState,
|
||||
uuid: '11',
|
||||
name: `Name-${EMPTY_UUID}`,
|
||||
id: '11',
|
||||
name: `Name-${NEW_VARIABLE_ID}`,
|
||||
index: 3,
|
||||
label: `Label-${EMPTY_UUID}`,
|
||||
label: `Label-${NEW_VARIABLE_ID}`,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -257,7 +258,7 @@ describe('sharedReducer', () => {
|
||||
],
|
||||
});
|
||||
const current = { text: ['A', 'B'], selected: true, value: ['A', 'B'] };
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'query' }, { option: current });
|
||||
const payload = toVariablePayload({ id: '0', type: 'query' }, { option: current });
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(sharedReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(setCurrentVariableValue(payload))
|
||||
@@ -287,7 +288,7 @@ describe('sharedReducer', () => {
|
||||
],
|
||||
});
|
||||
const current = { text: 'A + B', selected: true, value: ['A', 'B'] };
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'query' }, { option: current });
|
||||
const payload = toVariablePayload({ id: '0', type: 'query' }, { option: current });
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(sharedReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(setCurrentVariableValue(payload))
|
||||
@@ -317,7 +318,7 @@ describe('sharedReducer', () => {
|
||||
],
|
||||
});
|
||||
const current = { text: ALL_VARIABLE_TEXT, selected: true, value: [ALL_VARIABLE_VALUE] };
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'query' }, { option: current });
|
||||
const payload = toVariablePayload({ id: '0', type: 'query' }, { option: current });
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(sharedReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(setCurrentVariableValue(payload))
|
||||
@@ -340,7 +341,7 @@ describe('sharedReducer', () => {
|
||||
it('then state should be correct', () => {
|
||||
const adapter = createQueryVariableAdapter();
|
||||
const { initialState } = getVariableTestContext(adapter, {});
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'query' });
|
||||
const payload = toVariablePayload({ id: '0', type: 'query' });
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(sharedReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(addInitLock(payload))
|
||||
@@ -369,7 +370,7 @@ describe('sharedReducer', () => {
|
||||
} as unknown) as Deferred;
|
||||
const adapter = createQueryVariableAdapter();
|
||||
const { initialState } = getVariableTestContext(adapter, { initLock });
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'query' });
|
||||
const payload = toVariablePayload({ id: '0', type: 'query' });
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(sharedReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(resolveInitLock(payload))
|
||||
@@ -399,7 +400,7 @@ describe('sharedReducer', () => {
|
||||
} as unknown) as Deferred;
|
||||
const adapter = createQueryVariableAdapter();
|
||||
const { initialState } = getVariableTestContext(adapter, { initLock });
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'query' });
|
||||
const payload = toVariablePayload({ id: '0', type: 'query' });
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(sharedReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(removeInitLock(payload))
|
||||
@@ -417,9 +418,9 @@ describe('sharedReducer', () => {
|
||||
it('then state should be correct', () => {
|
||||
const adapter = createQueryVariableAdapter();
|
||||
const { initialState } = getVariableTestContext(adapter);
|
||||
const propName = 'name';
|
||||
const propValue = 'Updated name';
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'query' }, { propName, propValue });
|
||||
const propName = 'label';
|
||||
const propValue = 'Updated label';
|
||||
const payload = toVariablePayload({ id: '0', type: 'query' }, { propName, propValue });
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(sharedReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(changeVariableProp(payload))
|
||||
@@ -427,7 +428,26 @@ describe('sharedReducer', () => {
|
||||
...initialState,
|
||||
'0': {
|
||||
...initialState[0],
|
||||
name: 'Updated name',
|
||||
label: 'Updated label',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when changeVariableNameSucceeded is dispatched', () => {
|
||||
it('then state should be correct', () => {
|
||||
const adapter = createQueryVariableAdapter();
|
||||
const { initialState } = getVariableTestContext(adapter);
|
||||
const newName = 'A new name';
|
||||
const payload = toVariablePayload({ id: '0', type: 'query' }, { newName });
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(sharedReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(changeVariableNameSucceeded(payload))
|
||||
.thenStateShouldEqual({
|
||||
...initialState,
|
||||
'0': {
|
||||
...initialState[0],
|
||||
name: 'A new name',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@@ -2,42 +2,41 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
|
||||
import { VariableModel, VariableOption, VariableType, VariableWithOptions } from '../../templating/variable';
|
||||
import { AddVariable, ALL_VARIABLE_VALUE, EMPTY_UUID, getInstanceState, VariablePayload } from './types';
|
||||
import { AddVariable, ALL_VARIABLE_VALUE, getInstanceState, NEW_VARIABLE_ID, VariablePayload } from './types';
|
||||
import { variableAdapters } from '../adapters';
|
||||
import { changeVariableNameSucceeded } from '../editor/reducer';
|
||||
import { Deferred } from '../../../core/utils/deferred';
|
||||
import { initialVariablesState, VariablesState } from './variablesReducer';
|
||||
import { isQuery } from '../guard';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
const sharedReducerSlice = createSlice({
|
||||
name: 'templating/shared',
|
||||
initialState: initialVariablesState,
|
||||
reducers: {
|
||||
addVariable: (state: VariablesState, action: PayloadAction<VariablePayload<AddVariable>>) => {
|
||||
const uuid = action.payload.uuid ?? v4(); // for testing purposes we can call this with an uuid
|
||||
state[uuid] = {
|
||||
const id = action.payload.id ?? action.payload.data.model.name; // for testing purposes we can call this with an id
|
||||
state[id] = {
|
||||
...cloneDeep(variableAdapters.get(action.payload.type).initialState),
|
||||
...action.payload.data.model,
|
||||
};
|
||||
state[uuid].uuid = uuid;
|
||||
state[uuid].index = action.payload.data.index;
|
||||
state[uuid].global = action.payload.data.global;
|
||||
state[id].id = id;
|
||||
state[id].index = action.payload.data.index;
|
||||
state[id].global = action.payload.data.global;
|
||||
},
|
||||
addInitLock: (state: VariablesState, action: PayloadAction<VariablePayload>) => {
|
||||
const instanceState = getInstanceState(state, action.payload.uuid!);
|
||||
const instanceState = getInstanceState(state, action.payload.id!);
|
||||
instanceState.initLock = new Deferred();
|
||||
},
|
||||
resolveInitLock: (state: VariablesState, action: PayloadAction<VariablePayload>) => {
|
||||
const instanceState = getInstanceState(state, action.payload.uuid!);
|
||||
const instanceState = getInstanceState(state, action.payload.id!);
|
||||
instanceState.initLock?.resolve();
|
||||
},
|
||||
removeInitLock: (state: VariablesState, action: PayloadAction<VariablePayload>) => {
|
||||
const instanceState = getInstanceState(state, action.payload.uuid!);
|
||||
const instanceState = getInstanceState(state, action.payload.id!);
|
||||
instanceState.initLock = null;
|
||||
},
|
||||
removeVariable: (state: VariablesState, action: PayloadAction<VariablePayload<{ reIndex: boolean }>>) => {
|
||||
delete state[action.payload.uuid!];
|
||||
delete state[action.payload.id!];
|
||||
if (!action.payload.data.reIndex) {
|
||||
return;
|
||||
}
|
||||
@@ -47,15 +46,15 @@ const sharedReducerSlice = createSlice({
|
||||
variableStates[index].index = index;
|
||||
}
|
||||
},
|
||||
duplicateVariable: (state: VariablesState, action: PayloadAction<VariablePayload<{ newUuid: string }>>) => {
|
||||
const newUuid = action.payload.data?.newUuid ?? v4();
|
||||
const original = cloneDeep<VariableModel>(state[action.payload.uuid]);
|
||||
const index = Object.keys(state).length;
|
||||
duplicateVariable: (state: VariablesState, action: PayloadAction<VariablePayload<{ newId: string }>>) => {
|
||||
const original = cloneDeep<VariableModel>(state[action.payload.id]);
|
||||
const name = `copy_of_${original.name}`;
|
||||
state[newUuid] = {
|
||||
const newId = action.payload.data?.newId ?? name;
|
||||
const index = Object.keys(state).length;
|
||||
state[newId] = {
|
||||
...cloneDeep(variableAdapters.get(action.payload.type).initialState),
|
||||
...original,
|
||||
uuid: newUuid,
|
||||
id: newId,
|
||||
name,
|
||||
index,
|
||||
};
|
||||
@@ -69,30 +68,30 @@ const sharedReducerSlice = createSlice({
|
||||
const toVariable = variables.find(v => v.index === action.payload.data.toIndex);
|
||||
|
||||
if (fromVariable) {
|
||||
state[fromVariable.uuid!].index = action.payload.data.toIndex;
|
||||
state[fromVariable.id!].index = action.payload.data.toIndex;
|
||||
}
|
||||
|
||||
if (toVariable) {
|
||||
state[toVariable.uuid!].index = action.payload.data.fromIndex;
|
||||
state[toVariable.id!].index = action.payload.data.fromIndex;
|
||||
}
|
||||
},
|
||||
storeNewVariable: (state: VariablesState, action: PayloadAction<VariablePayload>) => {
|
||||
const uuid = action.payload.uuid!;
|
||||
const emptyVariable = cloneDeep<VariableModel>(state[EMPTY_UUID]);
|
||||
state[uuid!] = {
|
||||
const id = action.payload.id!;
|
||||
const emptyVariable = cloneDeep<VariableModel>(state[NEW_VARIABLE_ID]);
|
||||
state[id!] = {
|
||||
...cloneDeep(variableAdapters.get(action.payload.type).initialState),
|
||||
...emptyVariable,
|
||||
uuid,
|
||||
id,
|
||||
index: emptyVariable.index,
|
||||
};
|
||||
},
|
||||
changeVariableType: (state: VariablesState, action: PayloadAction<VariablePayload<{ newType: VariableType }>>) => {
|
||||
const { uuid } = action.payload;
|
||||
const { label, name, index } = state[uuid!];
|
||||
const { id } = action.payload;
|
||||
const { label, name, index } = state[id!];
|
||||
|
||||
state[uuid!] = {
|
||||
state[id!] = {
|
||||
...cloneDeep(variableAdapters.get(action.payload.data.newType).initialState),
|
||||
uuid,
|
||||
id: id,
|
||||
label,
|
||||
name,
|
||||
index,
|
||||
@@ -106,7 +105,7 @@ const sharedReducerSlice = createSlice({
|
||||
return;
|
||||
}
|
||||
|
||||
const instanceState = getInstanceState<VariableWithOptions>(state, action.payload.uuid);
|
||||
const instanceState = getInstanceState<VariableWithOptions>(state, action.payload.id);
|
||||
const current = { ...action.payload.data.option };
|
||||
|
||||
if (Array.isArray(current.text) && current.text.length > 0) {
|
||||
@@ -150,14 +149,14 @@ const sharedReducerSlice = createSlice({
|
||||
state: VariablesState,
|
||||
action: PayloadAction<VariablePayload<{ propName: string; propValue: any }>>
|
||||
) => {
|
||||
const instanceState = getInstanceState(state, action.payload.uuid!);
|
||||
const instanceState = getInstanceState(state, action.payload.id!);
|
||||
(instanceState as Record<string, any>)[action.payload.data.propName] = action.payload.data.propValue;
|
||||
},
|
||||
},
|
||||
extraReducers: builder =>
|
||||
builder.addCase(changeVariableNameSucceeded, (state, action) => {
|
||||
const instanceState = getInstanceState(state, action.payload.uuid);
|
||||
instanceState.name = action.payload.data;
|
||||
const instanceState = getInstanceState(state, action.payload.id);
|
||||
instanceState.name = action.payload.data.newName;
|
||||
}),
|
||||
});
|
||||
|
||||
|
@@ -1,19 +1,19 @@
|
||||
import { VariableModel, VariableType } from '../../templating/variable';
|
||||
import { VariablesState } from './variablesReducer';
|
||||
|
||||
export const EMPTY_UUID = '00000000-0000-0000-0000-000000000000';
|
||||
export const NEW_VARIABLE_ID = '00000000-0000-0000-0000-000000000000';
|
||||
export const ALL_VARIABLE_TEXT = 'All';
|
||||
export const ALL_VARIABLE_VALUE = '$__all';
|
||||
export const NONE_VARIABLE_TEXT = 'None';
|
||||
export const NONE_VARIABLE_VALUE = '';
|
||||
|
||||
export const getInstanceState = <Model extends VariableModel = VariableModel>(state: VariablesState, uuid: string) => {
|
||||
return state[uuid] as Model;
|
||||
export const getInstanceState = <Model extends VariableModel = VariableModel>(state: VariablesState, id: string) => {
|
||||
return state[id] as Model;
|
||||
};
|
||||
|
||||
export interface VariableIdentifier {
|
||||
type: VariableType;
|
||||
uuid: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface VariablePayload<T extends any = undefined> extends VariableIdentifier {
|
||||
@@ -27,7 +27,7 @@ export interface AddVariable<T extends VariableModel = VariableModel> {
|
||||
}
|
||||
|
||||
export const toVariableIdentifier = (variable: VariableModel): VariableIdentifier => {
|
||||
return { type: variable.type, uuid: variable.uuid! };
|
||||
return { type: variable.type, id: variable.id! };
|
||||
};
|
||||
|
||||
export function toVariablePayload<T extends any = undefined>(
|
||||
@@ -39,5 +39,5 @@ export function toVariablePayload<T extends any = undefined>(
|
||||
obj: VariableIdentifier | VariableModel,
|
||||
data?: T
|
||||
): VariablePayload<T> {
|
||||
return { type: obj.type, uuid: obj.uuid!, data: data as T };
|
||||
return { type: obj.type, id: obj.id!, data: data as T };
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ export const variablesReducer = (
|
||||
}
|
||||
|
||||
const variables = globalVariables.reduce((allVariables, state) => {
|
||||
allVariables[state.uuid!] = state;
|
||||
allVariables[state.id!] = state;
|
||||
return allVariables;
|
||||
}, {} as Record<string, VariableModel>);
|
||||
|
||||
|
@@ -23,7 +23,7 @@ describe('textbox actions', () => {
|
||||
|
||||
const variable: TextBoxVariableModel = {
|
||||
type: 'textbox',
|
||||
uuid: '0',
|
||||
id: '0',
|
||||
global: false,
|
||||
current: {
|
||||
value: '',
|
||||
|
@@ -8,7 +8,7 @@ import { toVariablePayload, VariableIdentifier } from '../state/types';
|
||||
export const updateTextBoxVariableOptions = (identifier: VariableIdentifier): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
await dispatch(createTextBoxOptions(toVariablePayload(identifier)));
|
||||
const variableInState = getVariable<TextBoxVariableModel>(identifier.uuid!, getState());
|
||||
const variableInState = getVariable<TextBoxVariableModel>(identifier.id!, getState());
|
||||
await variableAdapters.get(identifier.type).setValue(variableInState, variableInState.options[0], true);
|
||||
};
|
||||
};
|
||||
|
@@ -31,7 +31,7 @@ export const createTextBoxVariableAdapter = (): VariableAdapter<TextBoxVariableM
|
||||
await dispatch(updateTextBoxVariableOptions(toVariableIdentifier(variable)));
|
||||
},
|
||||
getSaveModel: variable => {
|
||||
const { index, uuid, initLock, global, ...rest } = cloneDeep(variable);
|
||||
const { index, id, initLock, global, ...rest } = cloneDeep(variable);
|
||||
return rest;
|
||||
},
|
||||
getValueForUrl: variable => {
|
||||
|
@@ -13,16 +13,16 @@ describe('textBoxVariableReducer', () => {
|
||||
describe('when createTextBoxOptions is dispatched', () => {
|
||||
it('then state should be correct', () => {
|
||||
const query = 'ABC';
|
||||
const uuid = '0';
|
||||
const { initialState } = getVariableTestContext(adapter, { uuid, query });
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'textbox' });
|
||||
const id = '0';
|
||||
const { initialState } = getVariableTestContext(adapter, { id, query });
|
||||
const payload = toVariablePayload({ id: '0', type: 'textbox' });
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(textBoxVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(createTextBoxOptions(payload))
|
||||
.thenStateShouldEqual({
|
||||
[uuid]: {
|
||||
...initialState[uuid],
|
||||
[id]: {
|
||||
...initialState[id],
|
||||
options: [
|
||||
{
|
||||
text: query,
|
||||
@@ -43,16 +43,16 @@ describe('textBoxVariableReducer', () => {
|
||||
describe('when createTextBoxOptions is dispatched and query contains spaces', () => {
|
||||
it('then state should be correct', () => {
|
||||
const query = ' ABC ';
|
||||
const uuid = '0';
|
||||
const { initialState } = getVariableTestContext(adapter, { uuid, query });
|
||||
const payload = toVariablePayload({ uuid: '0', type: 'textbox' });
|
||||
const id = '0';
|
||||
const { initialState } = getVariableTestContext(adapter, { id, query });
|
||||
const payload = toVariablePayload({ id: '0', type: 'textbox' });
|
||||
|
||||
reducerTester<VariablesState>()
|
||||
.givenReducer(textBoxVariableReducer, cloneDeep(initialState))
|
||||
.whenActionIsDispatched(createTextBoxOptions(payload))
|
||||
.thenStateShouldEqual({
|
||||
[uuid]: {
|
||||
...initialState[uuid],
|
||||
[id]: {
|
||||
...initialState[id],
|
||||
options: [
|
||||
{
|
||||
text: query.trim(),
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
import { TextBoxVariableModel, VariableHide, VariableOption } from '../../templating/variable';
|
||||
import { EMPTY_UUID, getInstanceState, VariablePayload } from '../state/types';
|
||||
import { getInstanceState, NEW_VARIABLE_ID, VariablePayload } from '../state/types';
|
||||
import { initialVariablesState, VariablesState } from '../state/variablesReducer';
|
||||
|
||||
export const initialTextBoxVariableModelState: TextBoxVariableModel = {
|
||||
uuid: EMPTY_UUID,
|
||||
id: NEW_VARIABLE_ID,
|
||||
global: false,
|
||||
index: -1,
|
||||
type: 'textbox',
|
||||
@@ -24,7 +24,7 @@ export const textBoxVariableSlice = createSlice({
|
||||
initialState: initialVariablesState,
|
||||
reducers: {
|
||||
createTextBoxOptions: (state: VariablesState, action: PayloadAction<VariablePayload>) => {
|
||||
const instanceState = getInstanceState<TextBoxVariableModel>(state, action.payload.uuid!);
|
||||
const instanceState = getInstanceState<TextBoxVariableModel>(state, action.payload.id!);
|
||||
instanceState.options = [
|
||||
{ text: instanceState.query.trim(), value: instanceState.query.trim(), selected: false },
|
||||
];
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import { AnyAction, configureStore, EnhancedStore, Reducer } from '@reduxjs/toolkit';
|
||||
import { Dispatch, Middleware, MiddlewareAPI } from 'redux';
|
||||
import thunk from 'redux-thunk';
|
||||
import { AnyAction, configureStore, EnhancedStore, Reducer } from '@reduxjs/toolkit';
|
||||
|
||||
import { StoreState } from '../../../app/types';
|
||||
import thunk from 'redux-thunk';
|
||||
import { setStore } from '../../../app/store/store';
|
||||
|
||||
export interface ReduxTesterGiven<State> {
|
||||
@@ -25,6 +25,7 @@ export interface ReduxTesterThen<State> {
|
||||
thenDispatchedActionsPredicateShouldEqual: (
|
||||
predicate: (dispatchedActions: AnyAction[]) => boolean
|
||||
) => ReduxTesterWhen<State>;
|
||||
thenNoActionsWhereDispatched: () => ReduxTesterWhen<State>;
|
||||
}
|
||||
|
||||
export interface ReduxTesterArguments<State> {
|
||||
@@ -117,12 +118,22 @@ export const reduxTester = <State>(args?: ReduxTesterArguments<State>): ReduxTes
|
||||
return instance;
|
||||
};
|
||||
|
||||
const thenNoActionsWhereDispatched = (): ReduxTesterWhen<State> => {
|
||||
if (debug) {
|
||||
console.log('Dispatched Actions', JSON.stringify(dispatchedActions, null, 2));
|
||||
}
|
||||
|
||||
expect(dispatchedActions.length).toBe(0);
|
||||
return instance;
|
||||
};
|
||||
|
||||
const instance = {
|
||||
givenRootReducer,
|
||||
whenActionIsDispatched,
|
||||
whenAsyncActionIsDispatched,
|
||||
thenDispatchedActionsShouldEqual,
|
||||
thenDispatchedActionsPredicateShouldEqual,
|
||||
thenNoActionsWhereDispatched,
|
||||
};
|
||||
|
||||
return instance;
|
||||
|
63
yarn.lock
63
yarn.lock
@@ -5928,11 +5928,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e"
|
||||
integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==
|
||||
|
||||
"@types/uuid@3.4.7":
|
||||
version "3.4.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.7.tgz#51d42247473bc00e38cc8dfaf70d936842a36c03"
|
||||
integrity sha512-C2j2FWgQkF1ru12SjZJyMaTPxs/f6n90+5G5qNakBxKXjTBc/YTSelHh4Pz1HUDwxFXD9WvpQhOGCDC+/Y4mIQ==
|
||||
|
||||
"@types/webpack-dev-server@*":
|
||||
version "3.1.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/webpack-dev-server/-/webpack-dev-server-3.1.7.tgz#a3e7a20366e68bc9853c730b56e994634cb78dac"
|
||||
@@ -11975,16 +11970,6 @@ find-cache-dir@^3.2.0:
|
||||
make-dir "^3.0.0"
|
||||
pkg-dir "^4.1.0"
|
||||
|
||||
find-parent-dir@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/find-parent-dir/-/find-parent-dir-0.3.0.tgz#33c44b429ab2b2f0646299c5f9f718f376ff8d54"
|
||||
integrity sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=
|
||||
|
||||
find-npm-prefix@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/find-npm-prefix/-/find-npm-prefix-1.0.2.tgz#8d8ce2c78b3b4b9e66c8acc6a37c231eb841cfdf"
|
||||
integrity sha512-KEftzJ+H90x6pcKtdXZEPsQse8/y/UnvzRKrOSQFprnrGaFuJ62fVkP34Iu2IYuMvyauCyoLTNkJZgrrGA2wkA==
|
||||
|
||||
find-replace@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-2.0.1.tgz#6d9683a7ca20f8f9aabeabad07e4e2580f528550"
|
||||
@@ -17364,14 +17349,7 @@ npm-packlist@^1.1.6, npm-packlist@^1.4.4:
|
||||
ignore-walk "^3.0.1"
|
||||
npm-bundled "^1.0.1"
|
||||
|
||||
npm-path@^2.0.2:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/npm-path/-/npm-path-2.0.4.tgz#c641347a5ff9d6a09e4d9bce5580c4f505278e64"
|
||||
integrity sha512-IFsj0R9C7ZdR5cP+ET342q77uSRdtWOlWpih5eC+lu29tIDbNEgDbzgVJ5UFvYHWhxDZ5TFkJafFioO0pPQjCw==
|
||||
dependencies:
|
||||
which "^1.2.10"
|
||||
|
||||
npm-pick-manifest@^3.0.0, npm-pick-manifest@^3.0.2:
|
||||
npm-pick-manifest@^3.0.0:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz#f4d9e5fd4be2153e5f4e5f9b7be8dc419a99abb7"
|
||||
integrity sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==
|
||||
@@ -17394,15 +17372,6 @@ npm-run-path@^4.0.0:
|
||||
dependencies:
|
||||
path-key "^3.0.0"
|
||||
|
||||
npm-which@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/npm-which/-/npm-which-3.0.1.tgz#9225f26ec3a285c209cae67c3b11a6b4ab7140aa"
|
||||
integrity sha1-kiXybsOihcIJyuZ8OxGmtKtxQKo=
|
||||
dependencies:
|
||||
commander "^2.9.0"
|
||||
npm-path "^2.0.2"
|
||||
which "^1.2.10"
|
||||
|
||||
"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.1, npmlog@^4.0.2, npmlog@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
|
||||
@@ -21777,7 +21746,7 @@ semver-regex@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-2.0.0.tgz#a93c2c5844539a770233379107b38c7b4ac9d338"
|
||||
integrity sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==
|
||||
|
||||
"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", "semver@^2.3.0 || 3.x || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1:
|
||||
"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
@@ -22660,11 +22629,6 @@ strict-uri-encode@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
||||
integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
|
||||
|
||||
strict-uri-encode@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
|
||||
integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY=
|
||||
|
||||
string-argv@0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da"
|
||||
@@ -24316,11 +24280,6 @@ utils-merge@1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
|
||||
|
||||
uuid@3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||
|
||||
uuid@^3.0.1, uuid@^3.1.0, uuid@^3.3.2:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"
|
||||
@@ -25207,24 +25166,6 @@ yargs@6.6.0:
|
||||
y18n "^3.2.1"
|
||||
yargs-parser "^4.2.0"
|
||||
|
||||
yargs@^11.0.0:
|
||||
version "11.1.1"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.1.tgz#5052efe3446a4df5ed669c995886cc0f13702766"
|
||||
integrity sha512-PRU7gJrJaXv3q3yQZ/+/X6KBswZiaQ+zOmdprZcouPYtQgvNU35i+68M4b1ZHLZtYFT5QObFLV+ZkmJYcwKdiw==
|
||||
dependencies:
|
||||
cliui "^4.0.0"
|
||||
decamelize "^1.1.1"
|
||||
find-up "^2.1.0"
|
||||
get-caller-file "^1.0.1"
|
||||
os-locale "^3.1.0"
|
||||
require-directory "^2.1.1"
|
||||
require-main-filename "^1.0.1"
|
||||
set-blocking "^2.0.0"
|
||||
string-width "^2.0.0"
|
||||
which-module "^2.0.0"
|
||||
y18n "^3.2.1"
|
||||
yargs-parser "^9.0.2"
|
||||
|
||||
yargs@^14.2.2:
|
||||
version "14.2.2"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.2.tgz#2769564379009ff8597cdd38fba09da9b493c4b5"
|
||||
|
Reference in New Issue
Block a user