Typed variables pt5: Remove generics from getInstanceState (#53018)

* wip

* make diff easier to read

* Update template_srv getVariables to return new TypedVariableModel

* update VariableType to use the type from TypedVariableModel

* tidy things up

* Chore: Use type-accurate mock variables in tests

* Chore: Type VariableState to use TypedVariableModel

* fix typo

* remove type assertion from template_srv.getVariables

* use typescript/no-redeclare for compatibility with ts overloads

* remove generics from getVariable() and overload it to only return undefined based on arguments

* update usages of getVariable()

* Remove generic from getInstanceState

* update usages of getInstanceState

* fix lint
This commit is contained in:
Josh Hunt
2022-08-10 16:06:49 +01:00
committed by GitHub
parent f32c0058a1
commit 081d6e9d3e
10 changed files with 59 additions and 28 deletions

View File

@ -6176,9 +6176,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/features/variables/state/selectors.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/features/variables/state/sharedReducer.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]

View File

@ -22,23 +22,37 @@ export const adHocVariableSlice = createSlice({
initialState: initialVariablesState,
reducers: {
filterAdded: (state: VariablesState, action: PayloadAction<VariablePayload<AdHocVariableFilter>>) => {
const instanceState = getInstanceState<AdHocVariableModel>(state, action.payload.id);
const instanceState = getInstanceState(state, action.payload.id);
if (instanceState.type !== 'adhoc') {
return;
}
instanceState.filters.push(action.payload.data);
},
filterRemoved: (state: VariablesState, action: PayloadAction<VariablePayload<number>>) => {
const instanceState = getInstanceState<AdHocVariableModel>(state, action.payload.id);
const index = action.payload.data;
const instanceState = getInstanceState(state, action.payload.id);
if (instanceState.type !== 'adhoc') {
return;
}
const index = action.payload.data;
instanceState.filters.splice(index, 1);
},
filterUpdated: (state: VariablesState, action: PayloadAction<VariablePayload<AdHocVariabelFilterUpdate>>) => {
const instanceState = getInstanceState<AdHocVariableModel>(state, action.payload.id);
const { filter, index } = action.payload.data;
const instanceState = getInstanceState(state, action.payload.id);
if (instanceState.type !== 'adhoc') {
return;
}
const { filter, index } = action.payload.data;
instanceState.filters[index] = filter;
},
filtersRestored: (state: VariablesState, action: PayloadAction<VariablePayload<AdHocVariableFilter[]>>) => {
const instanceState = getInstanceState<AdHocVariableModel>(state, action.payload.id);
const instanceState = getInstanceState(state, action.payload.id);
if (instanceState.type !== 'adhoc') {
return;
}
instanceState.filters = action.payload.data;
},
},

View File

@ -18,7 +18,11 @@ export const constantVariableSlice = createSlice({
initialState: initialVariablesState,
reducers: {
createConstantOptionsFromQuery: (state: VariablesState, action: PayloadAction<VariablePayload>) => {
const instanceState = getInstanceState<ConstantVariableModel>(state, action.payload.id);
const instanceState = getInstanceState(state, action.payload.id);
if (instanceState.type !== 'constant') {
return;
}
instanceState.options = [
{ text: instanceState.query.trim(), value: instanceState.query.trim(), selected: false },
];

View File

@ -21,7 +21,11 @@ export const customVariableSlice = createSlice({
initialState: initialVariablesState,
reducers: {
createCustomOptionsFromQuery: (state: VariablesState, action: PayloadAction<VariablePayload>) => {
const instanceState = getInstanceState<CustomVariableModel>(state, action.payload.id);
const instanceState = getInstanceState(state, action.payload.id);
if (instanceState.type !== 'custom') {
return;
}
const { includeAll, query } = instanceState;
const match = query.match(/(?:\\,|[^,])+/g) ?? [];

View File

@ -29,7 +29,11 @@ export const dataSourceVariableSlice = createSlice({
) => {
const { sources, regex } = action.payload.data;
const options: VariableOption[] = [];
const instanceState = getInstanceState<DataSourceVariableModel>(state, action.payload.id);
const instanceState = getInstanceState(state, action.payload.id);
if (instanceState.type !== 'datasource') {
return;
}
for (let i = 0; i < sources.length; i++) {
const source = sources[i];
// must match on type

View File

@ -22,7 +22,10 @@ export const intervalVariableSlice = createSlice({
initialState: initialVariablesState,
reducers: {
createIntervalOptions: (state: VariablesState, action: PayloadAction<VariablePayload>) => {
const instanceState = getInstanceState<IntervalVariableModel>(state, action.payload.id);
const instanceState = getInstanceState(state, action.payload.id);
if (instanceState.type !== 'interval') {
return;
}
const options: VariableOption[] = map(instanceState.query.match(/(["'])(.*?)\1|\w+/g), (text) => {
text = text.replace(/["']+/g, '');
return { text: text.trim(), value: text.trim(), selected: false };

View File

@ -141,7 +141,11 @@ export const queryVariableSlice = createSlice({
reducers: {
updateVariableOptions: (state: VariablesState, action: PayloadAction<VariablePayload<VariableOptionsUpdate>>) => {
const { results, templatedRegex } = action.payload.data;
const instanceState = getInstanceState<QueryVariableModel>(state, action.payload.id);
const instanceState = getInstanceState(state, action.payload.id);
if (instanceState.type !== 'query') {
return;
}
const { includeAll, sort } = instanceState;
const options = metricNamesToVariableValues(templatedRegex, sort, results);

View File

@ -1,6 +1,6 @@
import memoizeOne from 'memoize-one';
import { TypedVariableModel, VariableWithMultiSupport, VariableWithOptions } from '@grafana/data';
import { TypedVariableModel } from '@grafana/data';
import { getState } from '../../../store/store';
import { StoreState } from '../../../types';
@ -9,10 +9,6 @@ import { toStateKey } from '../utils';
import { getInitialTemplatingState, TemplatingState } from './reducers';
import { KeyedVariableIdentifier, VariablesState } from './types';
// TODO: this is just a temporary type until we remove generics from getInstanceState in a later PR
// we need to it satisfy the constraint of callers who specify VariableWithOptions or VariableWithMultiSupport
type GenericVariableModel = TypedVariableModel | VariableWithOptions | VariableWithMultiSupport;
export function getVariable(
identifier: KeyedVariableIdentifier,
state: StoreState,
@ -123,10 +119,6 @@ export function getVariableWithName(name: string, state: StoreState = getState()
return getVariable({ id: name, rootStateKey: lastKey, type: 'query' }, state, false);
}
// TODO: remove the generic and type assertion in a later PR
export function getInstanceState<Model extends GenericVariableModel = GenericVariableModel>(
state: VariablesState,
id: string
) {
return state[id] as Model;
export function getInstanceState(state: VariablesState, id: string) {
return state[id];
}

View File

@ -5,7 +5,8 @@ import { LoadingState, VariableType } from '@grafana/data';
import { variableAdapters } from '../adapters';
import { changeVariableNameSucceeded } from '../editor/reducer';
import { VariableModel, VariableOption, VariableWithOptions } from '../types';
import { hasOptions } from '../guard';
import { VariableModel, VariableOption } from '../types';
import { ensureStringValues } from '../utils';
import { getInstanceState, getNextVariableIndex } from './selectors';
@ -123,7 +124,11 @@ const sharedReducerSlice = createSlice({
return;
}
const instanceState = getInstanceState<VariableWithOptions>(state, action.payload.id);
const instanceState = getInstanceState(state, action.payload.id);
if (!hasOptions(instanceState)) {
return;
}
const { option } = action.payload.data;
const current = { ...option, text: ensureStringValues(option?.text), value: ensureStringValues(option?.value) };

View File

@ -18,7 +18,11 @@ export const textBoxVariableSlice = createSlice({
initialState: initialVariablesState,
reducers: {
createTextBoxOptions: (state: VariablesState, action: PayloadAction<VariablePayload>) => {
const instanceState = getInstanceState<TextBoxVariableModel>(state, action.payload.id);
const instanceState = getInstanceState(state, action.payload.id);
if (instanceState.type !== 'textbox') {
return;
}
const option = { text: instanceState.query.trim(), value: instanceState.query.trim(), selected: false };
instanceState.options = [option];
instanceState.current = option;