The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/public/app/features/query/components/QueryGroup.tsx

408 lines
11 KiB

// Libraries
import React, { PureComponent } from 'react';
// Components
import {
Button,
CustomScrollbar,
HorizontalGroup,
Icon,
InlineFormLabel,
Modal,
ScrollbarPosition,
stylesFactory,
Tooltip,
} from '@grafana/ui';
import { DataSourcePicker, getDataSourceSrv } from '@grafana/runtime';
import { QueryEditorRows } from './QueryEditorRows';
// Services
Chore: Remove angular dependency from backendSrv (#20999) * Chore: Remove angular dependency from backendSrv * Refactor: Naive soultion for logging out unauthorized users * Refactor: Restructures to different streams * Refactor: Restructures datasourceRequest * Refactor: Flipped back if statement * Refactor: Extracted getFromFetchStream * Refactor: Extracts toFailureStream operation * Refactor: Fixes issue when options.params contains arrays * Refactor: Fixes broken test (but we need a lot more) * Refactor: Adds explaining comments * Refactor: Adds latest RxJs version so cancellations work * Refactor: Cleans up the takeUntil code * Refactor: Adds tests for request function * Refactor: Separates into smaller functions * Refactor: Adds last error tests * Started to changed so we require getBackendSrv from the @grafana-runtime when applicable. * Using the getBackendSrv from @grafana/runtime. * Changed so we use the getBackendSrv from the @grafana-runtime when possible. * Fixed so Server Admin -> Orgs works again. * Removed unused dependency. * Fixed digest issues on the Server Admin -> Users page. * Fix: Fixes digest problems in Playlists * Fix: Fixes digest issues in VersionHistory * Tests: Fixes broken tests * Fix: Fixes digest issues in Alerting => Notification channels * Fixed digest issues on the Intive page. * Fixed so we run digest after password reset email sent. * Fixed digest issue when trying to sign up account. * Fixed so the Server Admin -> Edit Org works with backendSrv * Fixed so Server Admin -> Users works with backend srv. * Fixed digest issues in Server Admin -> Orgs * Fix: Fixes digest issues in DashList plugin * Fixed digest issues on Server Admin -> users. * Fix: Fixes digest issues with Snapshots * Fixed digest issue when deleting a user. * Fix: Fixes digest issues with dashLink * Chore: Changes RxJs version to 6.5.4 which includes the same cancellation fix * Fix: Fixes digest issue when toggling folder in manage dashboards * Fix: Fixes bug in executeInOrder * Fix: Fixes digest issue with CreateFolderCtrl and FolderDashboardsCtrl * Fix: Fixes tslint error in test * Refactor: Changes default behaviour for emitted messages as before migration * Fix: Fixes various digest issues when saving, starring or deleting dashboards * Fix: Fixes digest issues with FolderPickerCtrl * Fixed digest issue. * Fixed digest issues. * Fixed issues with angular digest. * Removed the this.digest pattern. Co-authored-by: Hugo Häggmark <hugo.haggmark@gmail.com> Co-authored-by: Marcus Andersson <systemvetaren@gmail.com>
5 years ago
import { backendSrv } from 'app/core/services/backend_srv';
import config from 'app/core/config';
// Types
import {
DataQuery,
DataSourceApi,
DataSourceInstanceSettings,
getDefaultTimeRange,
LoadingState,
PanelData,
} from '@grafana/data';
import { PluginHelp } from 'app/core/components/PluginHelp/PluginHelp';
import { addQuery, updateQueries } from 'app/core/utils/query';
import { Unsubscribable } from 'rxjs';
import { dataSource as expressionDatasource } from 'app/features/expressions/ExpressionDatasource';
import { selectors } from '@grafana/e2e-selectors';
import { PanelQueryRunner } from '../state/PanelQueryRunner';
import { QueryGroupOptionsEditor } from './QueryGroupOptions';
import { DashboardQueryEditor, isSharedDashboardQuery } from 'app/plugins/datasource/dashboard';
import { css } from '@emotion/css';
import { QueryGroupOptions } from 'app/types';
import { GroupActionComponents } from './QueryActionComponent';
interface Props {
queryRunner: PanelQueryRunner;
options: QueryGroupOptions;
onOpenQueryInspector?: () => void;
onRunQueries: () => void;
onOptionsChange: (options: QueryGroupOptions) => void;
}
interface State {
dataSource?: DataSourceApi;
dsSettings?: DataSourceInstanceSettings;
queries: DataQuery[];
helpContent: React.ReactNode;
isLoadingHelp: boolean;
7 years ago
isPickerOpen: boolean;
isAddingMixed: boolean;
scrollTop: number;
data: PanelData;
isHelpOpen: boolean;
defaultDataSource?: DataSourceApi;
}
export class QueryGroup extends PureComponent<Props, State> {
Chore: Remove angular dependency from backendSrv (#20999) * Chore: Remove angular dependency from backendSrv * Refactor: Naive soultion for logging out unauthorized users * Refactor: Restructures to different streams * Refactor: Restructures datasourceRequest * Refactor: Flipped back if statement * Refactor: Extracted getFromFetchStream * Refactor: Extracts toFailureStream operation * Refactor: Fixes issue when options.params contains arrays * Refactor: Fixes broken test (but we need a lot more) * Refactor: Adds explaining comments * Refactor: Adds latest RxJs version so cancellations work * Refactor: Cleans up the takeUntil code * Refactor: Adds tests for request function * Refactor: Separates into smaller functions * Refactor: Adds last error tests * Started to changed so we require getBackendSrv from the @grafana-runtime when applicable. * Using the getBackendSrv from @grafana/runtime. * Changed so we use the getBackendSrv from the @grafana-runtime when possible. * Fixed so Server Admin -> Orgs works again. * Removed unused dependency. * Fixed digest issues on the Server Admin -> Users page. * Fix: Fixes digest problems in Playlists * Fix: Fixes digest issues in VersionHistory * Tests: Fixes broken tests * Fix: Fixes digest issues in Alerting => Notification channels * Fixed digest issues on the Intive page. * Fixed so we run digest after password reset email sent. * Fixed digest issue when trying to sign up account. * Fixed so the Server Admin -> Edit Org works with backendSrv * Fixed so Server Admin -> Users works with backend srv. * Fixed digest issues in Server Admin -> Orgs * Fix: Fixes digest issues in DashList plugin * Fixed digest issues on Server Admin -> users. * Fix: Fixes digest issues with Snapshots * Fixed digest issue when deleting a user. * Fix: Fixes digest issues with dashLink * Chore: Changes RxJs version to 6.5.4 which includes the same cancellation fix * Fix: Fixes digest issue when toggling folder in manage dashboards * Fix: Fixes bug in executeInOrder * Fix: Fixes digest issue with CreateFolderCtrl and FolderDashboardsCtrl * Fix: Fixes tslint error in test * Refactor: Changes default behaviour for emitted messages as before migration * Fix: Fixes various digest issues when saving, starring or deleting dashboards * Fix: Fixes digest issues with FolderPickerCtrl * Fixed digest issue. * Fixed digest issues. * Fixed issues with angular digest. * Removed the this.digest pattern. Co-authored-by: Hugo Häggmark <hugo.haggmark@gmail.com> Co-authored-by: Marcus Andersson <systemvetaren@gmail.com>
5 years ago
backendSrv = backendSrv;
dataSourceSrv = getDataSourceSrv();
querySubscription: Unsubscribable | null = null;
state: State = {
isLoadingHelp: false,
helpContent: null,
isPickerOpen: false,
isAddingMixed: false,
isHelpOpen: false,
scrollTop: 0,
queries: [],
data: {
state: LoadingState.NotStarted,
series: [],
timeRange: getDefaultTimeRange(),
},
};
async componentDidMount() {
const { queryRunner, options } = this.props;
this.querySubscription = queryRunner.getData({ withTransforms: false, withFieldConfig: false }).subscribe({
QueryProcessing: Observable query interface and RxJS for query & stream processing (#18899) * I needed to learn some rxjs and understand this more, so just playing around * Updated * Removed all the complete calls * Refactoring * StreamHandler -> observable start * progress * simple singal works * Handle update time range * added error handling * wrap old function * minor changes * handle data format in the subscribe function * Use replay subject to return last value to subscribers * Set loading state after no response in 50ms * added missing file * updated comment * Added cancelation of network requests * runRequest: Added unit test scenario framework * Progress on tests * minor refactor of unit tests * updated test * removed some old code * Shared queries work again, and also became so much simplier * unified query and observe methods * implict any fix * Fixed closed subject issue * removed comment * Use last returned data for loading state * WIP: Explore to runRequest makover step1 * Minor progress * Minor progress on explore and runRequest * minor progress * Things are starting to work in explore * Updated prometheus to use new observable query response, greatly simplified code * Revert refId change * Found better solution for key/refId/requestId problem * use observable with loki * tests compile * fix loki query prep * Explore: correct first response handling * Refactorings * Refactoring * Explore: Fixes LoadingState and GraphResults between runs (#18986) * Refactor: Adds state to DataQueryResponse * Fix: Fixes so we do not empty results before new data arrives Fixes: #17409 * Transformations work * observable test data * remove single() from loki promise * Fixed comment * Explore: Fixes failing Loki and Prometheus unit tests (#18995) * Tests: Makes datasource tests work again * Fix: Fixes loki datasource so highligthing works * Chore: Runs Prettier * Fixed query runner tests * Delay loading state indication to 200ms * Fixed test * fixed unit tests * Clear cached calcs * Fixed bug getProcesedDataFrames * Fix the correct test is a better idea * Fix: Fixes so queries in Explore are only run if Graph/Table is shown (#19000) * Fix: Fixes so queries in Explore are only run if Graph/Table is shown Fixes: #18618 * Refactor: Removes unnecessary condition * PanelData: provide legacy data only when needed (#19018) * no legacy * invert logic... now compiles * merge getQueryResponseData and getDataRaw * update comment about query editor * use single getData() function * only send legacy when it is used in explore * pre process rather than post process * pre process rather than post process * Minor refactoring * Add missing tags to test datasource response * MixedDatasource: Adds query observable pattern to MixedDatasource (#19037) * start mixed datasource * Refactor: Refactors into observable parttern * Tests: Fixes tests * Tests: Removes console.log * Refactor: Adds unique requestId
6 years ago
next: (data: PanelData) => this.onPanelDataUpdate(data),
});
try {
const ds = await this.dataSourceSrv.get(options.dataSource.name);
const dsSettings = this.dataSourceSrv.getInstanceSettings(options.dataSource.name);
const defaultDataSource = await this.dataSourceSrv.get();
const queries = options.queries.map((q) => (q.datasource ? q : { ...q, datasource: dsSettings?.name }));
this.setState({ queries, dataSource: ds, dsSettings, defaultDataSource });
} catch (error) {
console.log('failed to load data source', error);
}
}
componentWillUnmount() {
if (this.querySubscription) {
this.querySubscription.unsubscribe();
this.querySubscription = null;
}
}
QueryProcessing: Observable query interface and RxJS for query & stream processing (#18899) * I needed to learn some rxjs and understand this more, so just playing around * Updated * Removed all the complete calls * Refactoring * StreamHandler -> observable start * progress * simple singal works * Handle update time range * added error handling * wrap old function * minor changes * handle data format in the subscribe function * Use replay subject to return last value to subscribers * Set loading state after no response in 50ms * added missing file * updated comment * Added cancelation of network requests * runRequest: Added unit test scenario framework * Progress on tests * minor refactor of unit tests * updated test * removed some old code * Shared queries work again, and also became so much simplier * unified query and observe methods * implict any fix * Fixed closed subject issue * removed comment * Use last returned data for loading state * WIP: Explore to runRequest makover step1 * Minor progress * Minor progress on explore and runRequest * minor progress * Things are starting to work in explore * Updated prometheus to use new observable query response, greatly simplified code * Revert refId change * Found better solution for key/refId/requestId problem * use observable with loki * tests compile * fix loki query prep * Explore: correct first response handling * Refactorings * Refactoring * Explore: Fixes LoadingState and GraphResults between runs (#18986) * Refactor: Adds state to DataQueryResponse * Fix: Fixes so we do not empty results before new data arrives Fixes: #17409 * Transformations work * observable test data * remove single() from loki promise * Fixed comment * Explore: Fixes failing Loki and Prometheus unit tests (#18995) * Tests: Makes datasource tests work again * Fix: Fixes loki datasource so highligthing works * Chore: Runs Prettier * Fixed query runner tests * Delay loading state indication to 200ms * Fixed test * fixed unit tests * Clear cached calcs * Fixed bug getProcesedDataFrames * Fix the correct test is a better idea * Fix: Fixes so queries in Explore are only run if Graph/Table is shown (#19000) * Fix: Fixes so queries in Explore are only run if Graph/Table is shown Fixes: #18618 * Refactor: Removes unnecessary condition * PanelData: provide legacy data only when needed (#19018) * no legacy * invert logic... now compiles * merge getQueryResponseData and getDataRaw * update comment about query editor * use single getData() function * only send legacy when it is used in explore * pre process rather than post process * pre process rather than post process * Minor refactoring * Add missing tags to test datasource response * MixedDatasource: Adds query observable pattern to MixedDatasource (#19037) * start mixed datasource * Refactor: Refactors into observable parttern * Tests: Fixes tests * Tests: Removes console.log * Refactor: Adds unique requestId
6 years ago
onPanelDataUpdate(data: PanelData) {
this.setState({ data });
}
onChangeDataSource = async (newSettings: DataSourceInstanceSettings) => {
const { dsSettings } = this.state;
const queries = updateQueries(newSettings, this.state.queries, expressionDatasource.name, dsSettings);
const dataSource = await this.dataSourceSrv.get(newSettings.name);
this.onChange({
queries,
dataSource: {
name: newSettings.name,
uid: newSettings.uid,
default: newSettings.isDefault,
},
});
this.setState({
queries,
dataSource: dataSource,
dsSettings: newSettings,
Inspector: move `Panel JSON` and query inspector to the inspector (#23354) * move Panel JSON to inspector * move Panel JSON to inspector * update test * use stats display options * move query inspector to inspector * open inspector from the queries section * subscribe to results * subscribe to results * open the right tab * apply review feedback * update menus (inspect tabs) * Dashboard: extend dashnav to add custom content (#23433) * Dashlist: Fixed dashlist broken in edit mode (#23426) * Chore: Fix bunch of strict null error to fix master CI (#23443) * Fix bunch of null error * Fix failing test * Another test fix * Docs: Add SQL region annotation examples (#23268) Add region annotation examples for SQL data sources in docs. Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com> * Docs: Update contributing doc to install node@12. (#23450) * NewPanelEdit: Minor style and description tweaks, AND PanelQueryRunner & autoMinMax (#23445) * NewPanelEdit: Minor style and description tweaks * Removed the worst snapshot of all time * ReactTable: adds color text to field options (#23427) * Feature: adds text color field config * Refactor: created an extension point * Refactor: uses HOC for extension instead * Fix: fixes background styling from affecting cells without display.color * Chore: export OptionsUIRegistryBuilder on grafana/data (#23444) * export the ui registry * add to utils index also * DataLinks: Do not full page reload data links links (#23429) * Templating: Fix global variable "__org.id" (#23362) * Fixed global variable __org.id value * correct orgId value * reverted the change as variables moved to new file * Chore: reduce null check errors to 788 (currently over 798) (#23449) * Fixed ts errors so build will succeed * Update packages/grafana-data/src/types/graph.ts Co-Authored-By: Ryan McKinley <ryantxu@gmail.com> * Feedback from code review * Leaving out trivial typing's * Fix error with color being undefined now. * fix test with timezone issue * Fixed test Co-authored-by: Ryan McKinley <ryantxu@gmail.com> Co-authored-by: Torkel Ödegaard <torkel@grafana.com> * Cloudwatch: prefer webIdentity over EC2 role (#23452) * Plugins: add a signature status flag (#23420) * Progress * fixed button * Final touches * now works from edit mode * fix layout * show raw objects * move query inspector buttons to the bottom * update snapshot * Updated design * Made full page reload work * Fixed minor style issue * Updated * More fixes * Removed unused imports * Updated * Moved to data tab out to seperate component * fixed ts issue Co-authored-by: Torkel Ödegaard <torkel@grafana.com> Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com> Co-authored-by: Alexandre de Verteuil <alexandre@grafana.com> Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com> Co-authored-by: Cyril Tovena <cyril.tovena@gmail.com> Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com> Co-authored-by: Vikky Omkar <vikkyomkar@gmail.com> Co-authored-by: Stephanie Closson <srclosson@gmail.com> Co-authored-by: Dário Nascimento <dfrnascimento@gmail.com>
5 years ago
});
};
onAddQueryClick = () => {
const { queries } = this.state;
this.onQueriesChange(addQuery(queries, this.newQuery()));
this.onScrollBottom();
};
newQuery(): Partial<DataQuery> {
const { dsSettings, defaultDataSource } = this.state;
if (!dsSettings?.meta.mixed) {
return { datasource: dsSettings?.name };
}
return {
datasource: defaultDataSource?.name,
};
}
onChange(changedProps: Partial<QueryGroupOptions>) {
this.props.onOptionsChange({
...this.props.options,
...changedProps,
});
}
onAddExpressionClick = () => {
this.onQueriesChange(addQuery(this.state.queries, expressionDatasource.newQuery()));
this.onScrollBottom();
};
onScrollBottom = () => {
this.setState({ scrollTop: 1000 });
};
onUpdateAndRun = (options: QueryGroupOptions) => {
this.props.onOptionsChange(options);
this.props.onRunQueries();
};
renderTopSection(styles: QueriesTabStyles) {
const { onOpenQueryInspector, options } = this.props;
const { dataSource, data } = this.state;
return (
<div>
<div className={styles.dataSourceRow}>
<InlineFormLabel htmlFor="data-source-picker" width={'auto'}>
Data source
</InlineFormLabel>
<div className={styles.dataSourceRowItem}>
<DataSourcePicker
onChange={this.onChangeDataSource}
current={options.dataSource.name}
metrics={true}
mixed={true}
dashboard={true}
variables={true}
/>
</div>
{dataSource && (
<>
<div className={styles.dataSourceRowItem}>
<Button
variant="secondary"
icon="question-circle"
title="Open data source help"
onClick={this.onOpenHelp}
/>
</div>
<div className={styles.dataSourceRowItemOptions}>
<QueryGroupOptionsEditor
options={options}
dataSource={dataSource}
data={data}
onChange={this.onUpdateAndRun}
/>
</div>
{onOpenQueryInspector && (
<div className={styles.dataSourceRowItem}>
<Button
variant="secondary"
onClick={onOpenQueryInspector}
aria-label={selectors.components.QueryTab.queryInspectorButton}
>
Query inspector
</Button>
</div>
)}
</>
)}
</div>
</div>
);
}
onOpenHelp = () => {
this.setState({ isHelpOpen: true });
};
onCloseHelp = () => {
this.setState({ isHelpOpen: false });
7 years ago
};
renderMixedPicker = () => {
return (
<DataSourcePicker
mixed={false}
onChange={this.onAddMixedQuery}
current={null}
autoFocus={true}
variables={true}
onBlur={this.onMixedPickerBlur}
openMenuOnFocus={true}
/>
);
};
onAddMixedQuery = (datasource: any) => {
this.onAddQuery({ datasource: datasource.name });
this.setState({ isAddingMixed: false, scrollTop: this.state.scrollTop + 10000 });
};
onMixedPickerBlur = () => {
this.setState({ isAddingMixed: false });
};
onAddQuery = (query: Partial<DataQuery>) => {
const { dsSettings, queries } = this.state;
this.onQueriesChange(addQuery(queries, query, dsSettings?.name));
this.onScrollBottom();
};
setScrollTop = ({ scrollTop }: ScrollbarPosition) => {
this.setState({ scrollTop: scrollTop });
};
onQueriesChange = (queries: DataQuery[]) => {
this.onChange({ queries });
this.setState({ queries });
};
renderQueries(dsSettings: DataSourceInstanceSettings) {
const { onRunQueries } = this.props;
const { data, queries } = this.state;
if (isSharedDashboardQuery(dsSettings.name)) {
return (
<DashboardQueryEditor
queries={queries}
panelData={data}
onChange={this.onQueriesChange}
onRunQueries={onRunQueries}
/>
);
}
return (
<div aria-label={selectors.components.QueryTab.content}>
<QueryEditorRows
queries={queries}
dsSettings={dsSettings}
onQueriesChange={this.onQueriesChange}
onAddQuery={this.onAddQuery}
onRunQueries={onRunQueries}
data={data}
/>
</div>
);
}
isExpressionsSupported(dsSettings: DataSourceInstanceSettings): boolean {
return (dsSettings.meta.alerting || dsSettings.meta.mixed) === true;
}
renderExtraActions() {
return GroupActionComponents.getAllExtraRenderAction().map((c, index) => {
return React.createElement(c, {
onAddQuery: this.onAddQuery,
onChangeDataSource: this.onChangeDataSource,
key: index,
});
});
}
renderAddQueryRow(dsSettings: DataSourceInstanceSettings, styles: QueriesTabStyles) {
const { isAddingMixed } = this.state;
const showAddButton = !(isAddingMixed || isSharedDashboardQuery(dsSettings.name));
return (
<HorizontalGroup spacing="md" align="flex-start">
{showAddButton && (
<Button
icon="plus"
onClick={this.onAddQueryClick}
variant="secondary"
aria-label={selectors.components.QueryTab.addQuery}
>
Query
</Button>
)}
{config.expressionsEnabled && this.isExpressionsSupported(dsSettings) && (
<Tooltip content="Beta feature: queries could stop working in next version" placement="right">
<Button
icon="plus"
onClick={this.onAddExpressionClick}
variant="secondary"
className={styles.expressionButton}
>
<span>Expression&nbsp;</span>
<Icon name="exclamation-triangle" className="muted" size="sm" />
</Button>
</Tooltip>
)}
{this.renderExtraActions()}
</HorizontalGroup>
);
}
render() {
const { scrollTop, isHelpOpen, dsSettings } = this.state;
const styles = getStyles();
return (
<CustomScrollbar autoHeightMin="100%" scrollTop={scrollTop} setScrollTop={this.setScrollTop}>
<div className={styles.innerWrapper}>
{this.renderTopSection(styles)}
{dsSettings && (
<>
<div className={styles.queriesWrapper}>{this.renderQueries(dsSettings)}</div>
{this.renderAddQueryRow(dsSettings, styles)}
{isHelpOpen && (
<Modal title="Data source help" isOpen={true} onDismiss={this.onCloseHelp}>
<PluginHelp plugin={dsSettings.meta} type="query_help" />
</Modal>
)}
</>
)}
</div>
</CustomScrollbar>
);
}
}
const getStyles = stylesFactory(() => {
const { theme } = config;
return {
innerWrapper: css`
display: flex;
flex-direction: column;
height: 100%;
padding: ${theme.spacing.md};
`,
dataSourceRow: css`
display: flex;
margin-bottom: ${theme.spacing.md};
`,
dataSourceRowItem: css`
margin-right: ${theme.spacing.inlineFormMargin};
`,
dataSourceRowItemOptions: css`
flex-grow: 1;
margin-right: ${theme.spacing.inlineFormMargin};
`,
queriesWrapper: css`
padding-bottom: 16px;
`,
expressionWrapper: css``,
expressionButton: css`
margin-right: ${theme.spacing.sm};
`,
};
});
type QueriesTabStyles = ReturnType<typeof getStyles>;