mirror of
https://github.com/grafana/grafana.git
synced 2025-01-17 04:02:50 -06:00
112a755e18
* Initial * WIP * wip * Refactor: fixing types * Refactor: Fixed more typings * Feature: Moves TestData to new API * Feature: Moves CloudMonitoringDatasource to new API * Feature: Moves PrometheusDatasource to new Variables API * Refactor: Clean up comments * Refactor: changes to QueryEditorProps instead * Refactor: cleans up testdata, prometheus and cloud monitoring variable support * Refactor: adds variableQueryRunner * Refactor: adds props to VariableQueryEditor * Refactor: reverted Loki editor * Refactor: refactor queryrunner into smaller pieces * Refactor: adds upgrade query thunk * Tests: Updates old tests * Docs: fixes build errors for exported api * Tests: adds guard tests * Tests: adds QueryRunner tests * Tests: fixes broken tests * Tests: adds variableQueryObserver tests * Test: adds tests for operator functions * Test: adds VariableQueryRunner tests * Refactor: renames dataSource * Refactor: adds definition for standard variable support * Refactor: adds cancellation to OptionPicker * Refactor: changes according to Dominiks suggestion * Refactor:tt * Refactor: adds tests for factories * Refactor: restructuring a bit * Refactor: renames variableQueryRunner.ts * Refactor: adds quick exit when runRequest returns errors * Refactor: using TextArea from grafana/ui * Refactor: changed from interfaces to classes instead * Tests: fixes broken test * Docs: fixes doc issue count * Docs: fixes doc issue count * Refactor: Adds check for self referencing queries * Tests: fixed unused variable * Refactor: Changes comments
193 lines
6.7 KiB
TypeScript
193 lines
6.7 KiB
TypeScript
import { DataQuery, DataSourceApi, DataSourcePluginMeta, DataSourceSelectItem } from '@grafana/data';
|
|
import { toDataQueryError } from '@grafana/runtime';
|
|
|
|
import { updateOptions } from '../state/actions';
|
|
import { QueryVariableModel } from '../types';
|
|
import { ThunkResult } from '../../../types';
|
|
import { getDatasourceSrv } from '../../plugins/datasource_srv';
|
|
import { getVariable } from '../state/selectors';
|
|
import { addVariableEditorError, changeVariableEditorExtended, removeVariableEditorError } from '../editor/reducer';
|
|
import { changeVariableProp } from '../state/sharedReducer';
|
|
import { toVariableIdentifier, toVariablePayload, VariableIdentifier } from '../state/types';
|
|
import { hasLegacyVariableSupport, hasStandardVariableSupport } from '../guard';
|
|
import { getVariableQueryEditor } from '../editor/getVariableQueryEditor';
|
|
import { Subscription } from 'rxjs';
|
|
import { getVariableQueryRunner } from './VariableQueryRunner';
|
|
import { variableQueryObserver } from './variableQueryObserver';
|
|
|
|
export const updateQueryVariableOptions = (
|
|
identifier: VariableIdentifier,
|
|
searchFilter?: string
|
|
): ThunkResult<void> => {
|
|
return async (dispatch, getState) => {
|
|
const variableInState = getVariable<QueryVariableModel>(identifier.id, getState());
|
|
try {
|
|
if (getState().templating.editor.id === variableInState.id) {
|
|
dispatch(removeVariableEditorError({ errorProp: 'update' }));
|
|
}
|
|
const datasource = await getDatasourceSrv().get(variableInState.datasource ?? '');
|
|
dispatch(upgradeLegacyQueries(identifier, datasource));
|
|
|
|
// we need to await the result from variableQueryRunner before moving on otherwise variables dependent on this
|
|
// variable will have the wrong current value as input
|
|
await new Promise((resolve, reject) => {
|
|
const subscription: Subscription = new Subscription();
|
|
const observer = variableQueryObserver(resolve, reject, subscription);
|
|
const responseSubscription = getVariableQueryRunner()
|
|
.getResponse(identifier)
|
|
.subscribe(observer);
|
|
subscription.add(responseSubscription);
|
|
|
|
getVariableQueryRunner().queueRequest({ identifier, datasource, searchFilter });
|
|
});
|
|
} catch (err) {
|
|
const error = toDataQueryError(err);
|
|
if (getState().templating.editor.id === variableInState.id) {
|
|
dispatch(addVariableEditorError({ errorProp: 'update', errorText: error.message }));
|
|
}
|
|
|
|
throw error;
|
|
}
|
|
};
|
|
};
|
|
|
|
export const initQueryVariableEditor = (identifier: VariableIdentifier): ThunkResult<void> => async (
|
|
dispatch,
|
|
getState
|
|
) => {
|
|
const dataSources: DataSourceSelectItem[] = getDatasourceSrv()
|
|
.getMetricSources()
|
|
.filter(ds => !ds.meta.mixed && ds.value !== null);
|
|
|
|
const defaultDatasource: DataSourceSelectItem = { name: '', value: '', meta: {} as DataSourcePluginMeta, sort: '' };
|
|
const allDataSources = [defaultDatasource].concat(dataSources);
|
|
dispatch(changeVariableEditorExtended({ propName: 'dataSources', propValue: allDataSources }));
|
|
|
|
const variable = getVariable<QueryVariableModel>(identifier.id, getState());
|
|
if (!variable.datasource) {
|
|
return;
|
|
}
|
|
await dispatch(changeQueryVariableDataSource(toVariableIdentifier(variable), variable.datasource));
|
|
};
|
|
|
|
export const changeQueryVariableDataSource = (
|
|
identifier: VariableIdentifier,
|
|
name: string | null
|
|
): ThunkResult<void> => {
|
|
return async (dispatch, getState) => {
|
|
try {
|
|
const dataSource = await getDatasourceSrv().get(name ?? '');
|
|
dispatch(changeVariableEditorExtended({ propName: 'dataSource', propValue: dataSource }));
|
|
|
|
const VariableQueryEditor = await getVariableQueryEditor(dataSource);
|
|
dispatch(changeVariableEditorExtended({ propName: 'VariableQueryEditor', propValue: VariableQueryEditor }));
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
};
|
|
};
|
|
|
|
export const changeQueryVariableQuery = (
|
|
identifier: VariableIdentifier,
|
|
query: any,
|
|
definition?: string
|
|
): ThunkResult<void> => async (dispatch, getState) => {
|
|
const variableInState = getVariable<QueryVariableModel>(identifier.id, getState());
|
|
if (hasSelfReferencingQuery(variableInState.name, query)) {
|
|
const errorText = 'Query cannot contain a reference to itself. Variable: $' + variableInState.name;
|
|
dispatch(addVariableEditorError({ errorProp: 'query', errorText }));
|
|
return;
|
|
}
|
|
|
|
dispatch(removeVariableEditorError({ errorProp: 'query' }));
|
|
dispatch(changeVariableProp(toVariablePayload(identifier, { propName: 'query', propValue: query })));
|
|
|
|
if (definition) {
|
|
dispatch(changeVariableProp(toVariablePayload(identifier, { propName: 'definition', propValue: definition })));
|
|
} else if (typeof query === 'string') {
|
|
dispatch(changeVariableProp(toVariablePayload(identifier, { propName: 'definition', propValue: query })));
|
|
}
|
|
|
|
await dispatch(updateOptions(identifier));
|
|
};
|
|
|
|
export function hasSelfReferencingQuery(name: string, query: any): boolean {
|
|
if (typeof query === 'string' && query.match(new RegExp('\\$' + name + '(/| |$)'))) {
|
|
return true;
|
|
}
|
|
|
|
const flattened = flattenQuery(query);
|
|
|
|
for (let prop in flattened) {
|
|
if (flattened.hasOwnProperty(prop)) {
|
|
const value = flattened[prop];
|
|
if (typeof value === 'string' && value.match(new RegExp('\\$' + name + '(/| |$)'))) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Function that takes any object and flattens all props into one level deep object
|
|
* */
|
|
export function flattenQuery(query: any): any {
|
|
if (typeof query !== 'object') {
|
|
return { query };
|
|
}
|
|
|
|
const keys = Object.keys(query);
|
|
const flattened = keys.reduce((all, key) => {
|
|
const value = query[key];
|
|
if (typeof value !== 'object') {
|
|
all[key] = value;
|
|
return all;
|
|
}
|
|
|
|
const result = flattenQuery(value);
|
|
for (let childProp in result) {
|
|
if (result.hasOwnProperty(childProp)) {
|
|
all[`${key}_${childProp}`] = result[childProp];
|
|
}
|
|
}
|
|
|
|
return all;
|
|
}, {} as Record<string, any>);
|
|
|
|
return flattened;
|
|
}
|
|
|
|
export function upgradeLegacyQueries(identifier: VariableIdentifier, datasource: DataSourceApi): ThunkResult<void> {
|
|
return function(dispatch, getState) {
|
|
if (hasLegacyVariableSupport(datasource)) {
|
|
return;
|
|
}
|
|
|
|
if (!hasStandardVariableSupport(datasource)) {
|
|
return;
|
|
}
|
|
|
|
const variable = getVariable<QueryVariableModel>(identifier.id, getState());
|
|
if (isDataQuery(variable.query)) {
|
|
return;
|
|
}
|
|
|
|
const query = {
|
|
refId: `${datasource.name}-${identifier.id}-Variable-Query`,
|
|
query: variable.query,
|
|
};
|
|
|
|
dispatch(changeVariableProp(toVariablePayload(identifier, { propName: 'query', propValue: query })));
|
|
};
|
|
}
|
|
|
|
function isDataQuery(query: any): query is DataQuery {
|
|
if (!query) {
|
|
return false;
|
|
}
|
|
|
|
return query.hasOwnProperty('refId') && typeof query.refId === 'string';
|
|
}
|