mirror of
https://github.com/grafana/grafana.git
synced 2025-08-06 20:59:35 +08:00
NgAlerting: View query result (#30218)
* Fix query preview add tabs to options * break out tabs to components * add refresh button * minor things after PR review * hide queries * Add simple error screen if there's an error * dropdown with different frames * move onrunqueries to redux * cleanup * show actual error
This commit is contained in:
@ -16,6 +16,7 @@ export const getNextRefIdChar = (queries: DataQuery[]): string => {
|
|||||||
export function addQuery(queries: DataQuery[], query?: Partial<DataQuery>): DataQuery[] {
|
export function addQuery(queries: DataQuery[], query?: Partial<DataQuery>): DataQuery[] {
|
||||||
const q = query || {};
|
const q = query || {};
|
||||||
q.refId = getNextRefIdChar(queries);
|
q.refId = getNextRefIdChar(queries);
|
||||||
|
q.hide = false;
|
||||||
return [...queries, q as DataQuery];
|
return [...queries, q as DataQuery];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import React, { FC, FormEvent } from 'react';
|
import React, { FC, FormEvent, useState } from 'react';
|
||||||
import { css } from 'emotion';
|
import { css } from 'emotion';
|
||||||
import { GrafanaTheme } from '@grafana/data';
|
import { GrafanaTheme } from '@grafana/data';
|
||||||
import { Field, Input, Select, TextArea, useStyles } from '@grafana/ui';
|
import { Field, Input, Tab, TabContent, TabsBar, TextArea, useStyles } from '@grafana/ui';
|
||||||
import { AlertDefinition, NotificationChannelType } from 'app/types';
|
import { AlertDefinition, NotificationChannelType } from 'app/types';
|
||||||
import { mapChannelsToSelectableValue } from '../utils/notificationChannels';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
alertDefinition: AlertDefinition;
|
alertDefinition: AlertDefinition;
|
||||||
@ -11,18 +10,46 @@ interface Props {
|
|||||||
onChange: (event: FormEvent) => void;
|
onChange: (event: FormEvent) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AlertDefinitionOptions: FC<Props> = ({ alertDefinition, notificationChannelTypes, onChange }) => {
|
enum Tabs {
|
||||||
|
Alert = 'alert',
|
||||||
|
Panel = 'panel',
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{ id: Tabs.Alert, text: 'Alert definition' },
|
||||||
|
{ id: Tabs.Panel, text: 'Panel' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const AlertDefinitionOptions: FC<Props> = ({ alertDefinition, onChange }) => {
|
||||||
const styles = useStyles(getStyles);
|
const styles = useStyles(getStyles);
|
||||||
|
const [activeTab, setActiveTab] = useState<string>(Tabs.Alert);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ paddingTop: '16px' }}>
|
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<h4>Alert definition</h4>
|
<TabsBar>
|
||||||
|
{tabs.map((tab, index) => (
|
||||||
|
<Tab
|
||||||
|
key={`${tab.id}-${index}`}
|
||||||
|
label={tab.text}
|
||||||
|
active={tab.id === activeTab}
|
||||||
|
onChangeTab={() => setActiveTab(tab.id)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</TabsBar>
|
||||||
|
<TabContent className={styles.tabContent}>
|
||||||
|
{activeTab === Tabs.Alert && (
|
||||||
|
<div>
|
||||||
<Field label="Name">
|
<Field label="Name">
|
||||||
<Input width={25} name="name" value={alertDefinition.name} onChange={onChange} />
|
<Input width={25} name="name" value={alertDefinition.name} onChange={onChange} />
|
||||||
</Field>
|
</Field>
|
||||||
<Field label="Description" description="What does the alert do and why was it created">
|
<Field label="Description" description="What does the alert do and why was it created">
|
||||||
<TextArea rows={5} width={25} name="description" value={alertDefinition.description} onChange={onChange} />
|
<TextArea
|
||||||
|
rows={5}
|
||||||
|
width={25}
|
||||||
|
name="description"
|
||||||
|
value={alertDefinition.description}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
<Field label="Evaluate">
|
<Field label="Evaluate">
|
||||||
<span>Every For</span>
|
<span>Every For</span>
|
||||||
@ -30,26 +57,23 @@ export const AlertDefinitionOptions: FC<Props> = ({ alertDefinition, notificatio
|
|||||||
<Field label="Conditions">
|
<Field label="Conditions">
|
||||||
<div></div>
|
<div></div>
|
||||||
</Field>
|
</Field>
|
||||||
{notificationChannelTypes.length > 0 && (
|
|
||||||
<>
|
|
||||||
<Field label="Notification channel">
|
|
||||||
<Select options={mapChannelsToSelectableValue(notificationChannelTypes, false)} onChange={onChange} />
|
|
||||||
</Field>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
{activeTab === Tabs.Panel && <div>VizPicker</div>}
|
||||||
|
</TabContent>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme) => {
|
const getStyles = (theme: GrafanaTheme) => {
|
||||||
return {
|
return {
|
||||||
wrapper: css`
|
|
||||||
padding-top: ${theme.spacing.md};
|
|
||||||
`,
|
|
||||||
container: css`
|
container: css`
|
||||||
padding: ${theme.spacing.md};
|
margin-top: ${theme.spacing.md};
|
||||||
background-color: ${theme.colors.panelBg};
|
height: 100%;
|
||||||
|
`,
|
||||||
|
tabContent: css`
|
||||||
|
background: ${theme.colors.panelBg};
|
||||||
|
height: 100%;
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux';
|
import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux';
|
||||||
import { css } from 'emotion';
|
import { css } from 'emotion';
|
||||||
import { dateMath, GrafanaTheme } from '@grafana/data';
|
import { GrafanaTheme } from '@grafana/data';
|
||||||
import { stylesFactory } from '@grafana/ui';
|
import { RefreshPicker, stylesFactory } from '@grafana/ui';
|
||||||
import { config } from 'app/core/config';
|
import { config } from 'app/core/config';
|
||||||
import { QueryGroup } from '../../query/components/QueryGroup';
|
import { QueryGroup } from '../../query/components/QueryGroup';
|
||||||
import { PanelQueryRunner } from '../../query/state/PanelQueryRunner';
|
import { PanelQueryRunner } from '../../query/state/PanelQueryRunner';
|
||||||
import { QueryGroupOptions } from '../../query/components/QueryGroupOptions';
|
import { onRunQueries, queryOptionsChange } from '../state/actions';
|
||||||
import { queryOptionsChange } from '../state/actions';
|
import { QueryGroupOptions, StoreState } from 'app/types';
|
||||||
import { StoreState } from '../../../types';
|
|
||||||
|
|
||||||
interface OwnProps {}
|
interface OwnProps {}
|
||||||
|
|
||||||
@ -18,6 +17,7 @@ interface ConnectedProps {
|
|||||||
}
|
}
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
queryOptionsChange: typeof queryOptionsChange;
|
queryOptionsChange: typeof queryOptionsChange;
|
||||||
|
onRunQueries: typeof onRunQueries;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = ConnectedProps & DispatchProps & OwnProps;
|
type Props = ConnectedProps & DispatchProps & OwnProps;
|
||||||
@ -28,17 +28,11 @@ export class AlertingQueryEditor extends PureComponent<Props> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onRunQueries = () => {
|
onRunQueries = () => {
|
||||||
const { queryRunner, queryOptions } = this.props;
|
this.props.onRunQueries();
|
||||||
const timeRange = { from: 'now-1h', to: 'now' };
|
};
|
||||||
|
|
||||||
queryRunner.run({
|
onIntervalChanged = (interval: string) => {
|
||||||
timezone: 'browser',
|
this.props.queryOptionsChange({ ...this.props.queryOptions, minInterval: interval });
|
||||||
timeRange: { from: dateMath.parse(timeRange.from)!, to: dateMath.parse(timeRange.to)!, raw: timeRange },
|
|
||||||
maxDataPoints: queryOptions.maxDataPoints ?? 100,
|
|
||||||
minInterval: queryOptions.minInterval,
|
|
||||||
queries: queryOptions.queries,
|
|
||||||
datasource: queryOptions.dataSource.name!,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -49,6 +43,13 @@ export class AlertingQueryEditor extends PureComponent<Props> {
|
|||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<h4>Queries</h4>
|
<h4>Queries</h4>
|
||||||
|
<div className={styles.refreshWrapper}>
|
||||||
|
<RefreshPicker
|
||||||
|
onIntervalChanged={this.onIntervalChanged}
|
||||||
|
onRefresh={this.onRunQueries}
|
||||||
|
intervals={['15s', '30s']}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<QueryGroup
|
<QueryGroup
|
||||||
queryRunner={queryRunner}
|
queryRunner={queryRunner}
|
||||||
options={queryOptions}
|
options={queryOptions}
|
||||||
@ -70,6 +71,7 @@ const mapStateToProps: MapStateToProps<ConnectedProps, OwnProps, StoreState> = s
|
|||||||
|
|
||||||
const mapDispatchToProps: MapDispatchToProps<DispatchProps, OwnProps> = {
|
const mapDispatchToProps: MapDispatchToProps<DispatchProps, OwnProps> = {
|
||||||
queryOptionsChange,
|
queryOptionsChange,
|
||||||
|
onRunQueries,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(AlertingQueryEditor);
|
export default connect(mapStateToProps, mapDispatchToProps)(AlertingQueryEditor);
|
||||||
@ -85,6 +87,10 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
|||||||
background-color: ${theme.colors.panelBg};
|
background-color: ${theme.colors.panelBg};
|
||||||
height: 100%;
|
height: 100%;
|
||||||
`,
|
`,
|
||||||
|
refreshWrapper: css`
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
`,
|
||||||
editorWrapper: css`
|
editorWrapper: css`
|
||||||
border: 1px solid ${theme.colors.panelBorder};
|
border: 1px solid ${theme.colors.panelBorder};
|
||||||
border-radius: ${theme.border.radius.md};
|
border-radius: ${theme.border.radius.md};
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
import React, { FC, useMemo, useState } from 'react';
|
import React, { FC, useMemo, useState } from 'react';
|
||||||
import { useObservable } from 'react-use';
|
import { useObservable } from 'react-use';
|
||||||
import { css } from 'emotion';
|
import { css } from 'emotion';
|
||||||
|
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||||
import { GrafanaTheme } from '@grafana/data';
|
import { GrafanaTheme } from '@grafana/data';
|
||||||
import { TabsBar, TabContent, Tab, useStyles, Table } from '@grafana/ui';
|
import { TabsBar, TabContent, Tab, useStyles, Icon } from '@grafana/ui';
|
||||||
import { PanelQueryRunner } from '../../query/state/PanelQueryRunner';
|
import { PanelQueryRunner } from '../../query/state/PanelQueryRunner';
|
||||||
|
import { PreviewQueryTab } from './PreviewQueryTab';
|
||||||
|
import { PreviewInstancesTab } from './PreviewInstancesTab';
|
||||||
|
|
||||||
enum Tabs {
|
enum Tabs {
|
||||||
Query = 'query',
|
Query = 'query',
|
||||||
Instance = 'instance',
|
Instances = 'instances',
|
||||||
}
|
}
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{ id: Tabs.Query, text: 'Query', active: true },
|
{ id: Tabs.Query, text: 'Query result' },
|
||||||
{ id: Tabs.Instance, text: 'Alerting instance', active: false },
|
{ id: Tabs.Instances, text: 'Alerting instances' },
|
||||||
];
|
];
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -20,11 +23,12 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const AlertingQueryPreview: FC<Props> = ({ queryRunner }) => {
|
export const AlertingQueryPreview: FC<Props> = ({ queryRunner }) => {
|
||||||
const [activeTab, setActiveTab] = useState<string>('query');
|
const [activeTab, setActiveTab] = useState<string>(Tabs.Query);
|
||||||
const styles = useStyles(getStyles);
|
const styles = useStyles(getStyles);
|
||||||
|
|
||||||
const observable = useMemo(() => queryRunner.getData({ withFieldConfig: true, withTransforms: true }), []);
|
const observable = useMemo(() => queryRunner.getData({ withFieldConfig: true, withTransforms: true }), []);
|
||||||
const data = useObservable(observable);
|
const data = useObservable(observable);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
<TabsBar>
|
<TabsBar>
|
||||||
@ -40,30 +44,65 @@ export const AlertingQueryPreview: FC<Props> = ({ queryRunner }) => {
|
|||||||
})}
|
})}
|
||||||
</TabsBar>
|
</TabsBar>
|
||||||
<TabContent className={styles.tabContent}>
|
<TabContent className={styles.tabContent}>
|
||||||
{activeTab === Tabs.Query && data && (
|
{data && data.state === 'Error' ? (
|
||||||
|
<div className={styles.noQueries}>
|
||||||
|
<h4 className={styles.noQueriesHeader}>There was an error :(</h4>
|
||||||
|
<div>{data.error?.data?.error}</div>
|
||||||
|
</div>
|
||||||
|
) : data && data.series.length > 0 ? (
|
||||||
|
<AutoSizer style={{ width: '100%', height: '100%' }}>
|
||||||
|
{({ width, height }) => {
|
||||||
|
switch (activeTab) {
|
||||||
|
case Tabs.Instances:
|
||||||
|
return <PreviewInstancesTab isTested={false} data={data} styles={styles} />;
|
||||||
|
|
||||||
|
case Tabs.Query:
|
||||||
|
default:
|
||||||
|
return <PreviewQueryTab data={data} width={width} height={height} />;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
</AutoSizer>
|
||||||
|
) : (
|
||||||
|
<div className={styles.noQueries}>
|
||||||
|
<h4 className={styles.noQueriesHeader}>No queries added.</h4>
|
||||||
|
<div>Start adding queries to this alert and a visualisation for your queries will appear here.</div>
|
||||||
<div>
|
<div>
|
||||||
<Table data={data.series[0]} width={1200} height={300} />
|
Learn more about how to create alert definitions <Icon name="external-link-alt" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{activeTab === Tabs.Instance && <div>Instance something something dark side</div>}
|
|
||||||
</TabContent>
|
</TabContent>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme) => {
|
const getStyles = (theme: GrafanaTheme) => {
|
||||||
const tabBarHeight = 42;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
wrapper: css`
|
wrapper: css`
|
||||||
label: alertDefinitionPreviewTabs;
|
label: alertDefinitionPreviewTabs;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: ${theme.spacing.md} 0 0 ${theme.spacing.md};
|
padding: ${theme.spacing.md} 0 0 ${theme.spacing.md};
|
||||||
`,
|
`,
|
||||||
tabContent: css`
|
tabContent: css`
|
||||||
background: ${theme.colors.panelBg};
|
background: ${theme.colors.panelBg};
|
||||||
height: calc(100% - ${tabBarHeight}px);
|
height: 100%;
|
||||||
|
`,
|
||||||
|
noQueries: css`
|
||||||
|
color: ${theme.colors.textSemiWeak};
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
`,
|
||||||
|
noQueriesHeader: css`
|
||||||
|
color: ${theme.colors.textSemiWeak};
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PreviewStyles = ReturnType<typeof getStyles>;
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
import React, { FC } from 'react';
|
||||||
|
import { PanelData } from '@grafana/data';
|
||||||
|
import { Button } from '@grafana/ui';
|
||||||
|
import { PreviewStyles } from './AlertingQueryPreview';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: PanelData;
|
||||||
|
isTested: boolean;
|
||||||
|
styles: PreviewStyles;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PreviewInstancesTab: FC<Props> = ({ data, isTested, styles }) => {
|
||||||
|
if (!isTested) {
|
||||||
|
return (
|
||||||
|
<div className={styles.noQueries}>
|
||||||
|
<h4 className={styles.noQueriesHeader}>You haven’t tested your alert yet.</h4>
|
||||||
|
<div>In order to see your instances, you need to test your alert first.</div>
|
||||||
|
<Button>Test alert now</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <div>Instances</div>;
|
||||||
|
};
|
52
public/app/features/alerting/components/PreviewQueryTab.tsx
Normal file
52
public/app/features/alerting/components/PreviewQueryTab.tsx
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import React, { FC, useMemo, useState } from 'react';
|
||||||
|
import { getFrameDisplayName, GrafanaTheme, PanelData } from '@grafana/data';
|
||||||
|
import { Select, stylesFactory, Table, useTheme } from '@grafana/ui';
|
||||||
|
import { css } from 'emotion';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: PanelData;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PreviewQueryTab: FC<Props> = ({ data, height, width }) => {
|
||||||
|
const [currentSeries, setSeries] = useState<number>(0);
|
||||||
|
const theme = useTheme();
|
||||||
|
const styles = getStyles(theme, height);
|
||||||
|
const series = useMemo(
|
||||||
|
() => data.series.map((frame, index) => ({ value: index, label: getFrameDisplayName(frame) })),
|
||||||
|
[data.series]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Select padding
|
||||||
|
const padding = 16;
|
||||||
|
|
||||||
|
if (data.series.length > 1) {
|
||||||
|
return (
|
||||||
|
<div className={styles.wrapper}>
|
||||||
|
<div style={{ height: height - theme.spacing.formInputHeight - 16 }}>
|
||||||
|
<Table
|
||||||
|
data={data.series[currentSeries]}
|
||||||
|
height={height - theme.spacing.formInputHeight - padding}
|
||||||
|
width={width}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles.selectWrapper}>
|
||||||
|
<Select onChange={selectedValue => setSeries(selectedValue.value!)} options={series} value={currentSeries} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <Table data={data.series[0]} height={height} width={width} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStyles = stylesFactory((theme: GrafanaTheme, height: number) => {
|
||||||
|
return {
|
||||||
|
wrapper: css`
|
||||||
|
height: ${height}px;
|
||||||
|
`,
|
||||||
|
selectWrapper: css`
|
||||||
|
padding: ${theme.spacing.md};
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
});
|
@ -1,4 +1,4 @@
|
|||||||
import { AppEvents } from '@grafana/data';
|
import { AppEvents, dateMath } from '@grafana/data';
|
||||||
import { getBackendSrv } from '@grafana/runtime';
|
import { getBackendSrv } from '@grafana/runtime';
|
||||||
import { appEvents } from 'app/core/core';
|
import { appEvents } from 'app/core/core';
|
||||||
import { updateLocation } from 'app/core/actions';
|
import { updateLocation } from 'app/core/actions';
|
||||||
@ -14,7 +14,7 @@ import {
|
|||||||
setQueryOptions,
|
setQueryOptions,
|
||||||
} from './reducers';
|
} from './reducers';
|
||||||
import { AlertDefinition, AlertDefinitionUiState, AlertRuleDTO, NotifierDTO, ThunkResult } from 'app/types';
|
import { AlertDefinition, AlertDefinitionUiState, AlertRuleDTO, NotifierDTO, ThunkResult } from 'app/types';
|
||||||
import { QueryGroupOptions } from '../../query/components/QueryGroupOptions';
|
import { QueryGroupOptions } from 'app/types';
|
||||||
|
|
||||||
export function getAlertRulesAsync(options: { state: string }): ThunkResult<void> {
|
export function getAlertRulesAsync(options: { state: string }): ThunkResult<void> {
|
||||||
return async dispatch => {
|
return async dispatch => {
|
||||||
@ -138,3 +138,19 @@ export function queryOptionsChange(queryOptions: QueryGroupOptions): ThunkResult
|
|||||||
dispatch(setQueryOptions(queryOptions));
|
dispatch(setQueryOptions(queryOptions));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function onRunQueries(): ThunkResult<void> {
|
||||||
|
return (dispatch, getStore) => {
|
||||||
|
const { queryRunner, queryOptions } = getStore().alertDefinition;
|
||||||
|
const timeRange = { from: 'now-1h', to: 'now' };
|
||||||
|
|
||||||
|
queryRunner.run({
|
||||||
|
timezone: 'browser',
|
||||||
|
timeRange: { from: dateMath.parse(timeRange.from)!, to: dateMath.parse(timeRange.to)!, raw: timeRange },
|
||||||
|
maxDataPoints: queryOptions.maxDataPoints ?? 100,
|
||||||
|
minInterval: queryOptions.minInterval,
|
||||||
|
queries: queryOptions.queries,
|
||||||
|
datasource: queryOptions.dataSource.name!,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -12,11 +12,11 @@ import {
|
|||||||
NotificationChannelOption,
|
NotificationChannelOption,
|
||||||
NotificationChannelState,
|
NotificationChannelState,
|
||||||
NotifierDTO,
|
NotifierDTO,
|
||||||
|
QueryGroupOptions,
|
||||||
} from 'app/types';
|
} from 'app/types';
|
||||||
import store from 'app/core/store';
|
import store from 'app/core/store';
|
||||||
import { config } from '@grafana/runtime';
|
import { config } from '@grafana/runtime';
|
||||||
import { PanelQueryRunner } from '../../query/state/PanelQueryRunner';
|
import { PanelQueryRunner } from '../../query/state/PanelQueryRunner';
|
||||||
import { QueryGroupOptions } from '../../query/components/QueryGroupOptions';
|
|
||||||
|
|
||||||
export const ALERT_DEFINITION_UI_STATE_STORAGE_KEY = 'grafana.alerting.alertDefinition.ui';
|
export const ALERT_DEFINITION_UI_STATE_STORAGE_KEY = 'grafana.alerting.alertDefinition.ui';
|
||||||
const DEFAULT_ALERT_DEFINITION_UI_STATE: AlertDefinitionUiState = { rightPaneSize: 400, topPaneSize: 0.45 };
|
const DEFAULT_ALERT_DEFINITION_UI_STATE: AlertDefinitionUiState = { rightPaneSize: 400, topPaneSize: 0.45 };
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { QueryGroup } from 'app/features/query/components/QueryGroup';
|
import { QueryGroup } from 'app/features/query/components/QueryGroup';
|
||||||
import { QueryGroupOptions } from 'app/features/query/components/QueryGroupOptions';
|
|
||||||
import { PanelModel } from '../../state';
|
import { PanelModel } from '../../state';
|
||||||
import { getLocationSrv } from '@grafana/runtime';
|
import { getLocationSrv } from '@grafana/runtime';
|
||||||
|
import { QueryGroupOptions } from 'app/types';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
panel: PanelModel;
|
panel: PanelModel;
|
||||||
|
@ -221,9 +221,9 @@ export class QueryEditorRow extends PureComponent<Props, State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onDisableQuery = () => {
|
onDisableQuery = () => {
|
||||||
this.props.query.hide = !this.props.query.hide;
|
const { query } = this.props;
|
||||||
|
this.props.onChange({ ...query, hide: !query.hide });
|
||||||
this.props.onRunQuery();
|
this.props.onRunQuery();
|
||||||
this.forceUpdate();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
renderCollapsedText(): string | null {
|
renderCollapsedText(): string | null {
|
||||||
|
@ -23,9 +23,10 @@ import { Unsubscribable } from 'rxjs';
|
|||||||
import { expressionDatasource, ExpressionDatasourceID } from 'app/features/expressions/ExpressionDatasource';
|
import { expressionDatasource, ExpressionDatasourceID } from 'app/features/expressions/ExpressionDatasource';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { PanelQueryRunner } from '../state/PanelQueryRunner';
|
import { PanelQueryRunner } from '../state/PanelQueryRunner';
|
||||||
import { QueryGroupOptions, QueryGroupOptionsEditor } from './QueryGroupOptions';
|
import { QueryGroupOptionsEditor } from './QueryGroupOptions';
|
||||||
import { DashboardQueryEditor, isSharedDashboardQuery } from 'app/plugins/datasource/dashboard';
|
import { DashboardQueryEditor, isSharedDashboardQuery } from 'app/plugins/datasource/dashboard';
|
||||||
import { css } from 'emotion';
|
import { css } from 'emotion';
|
||||||
|
import { QueryGroupOptions } from 'app/types';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
queryRunner: PanelQueryRunner;
|
queryRunner: PanelQueryRunner;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import React, { PureComponent, ChangeEvent, FocusEvent } from 'react';
|
import React, { PureComponent, ChangeEvent, FocusEvent } from 'react';
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
import { rangeUtil, PanelData, DataSourceApi, DataQuery } from '@grafana/data';
|
import { rangeUtil, PanelData, DataSourceApi } from '@grafana/data';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Switch, Input, InlineField, InlineFormLabel, stylesFactory } from '@grafana/ui';
|
import { Switch, Input, InlineField, InlineFormLabel, stylesFactory } from '@grafana/ui';
|
||||||
@ -11,25 +11,7 @@ import { Switch, Input, InlineField, InlineFormLabel, stylesFactory } from '@gra
|
|||||||
import { QueryOperationRow } from 'app/core/components/QueryOperationRow/QueryOperationRow';
|
import { QueryOperationRow } from 'app/core/components/QueryOperationRow/QueryOperationRow';
|
||||||
import { config } from 'app/core/config';
|
import { config } from 'app/core/config';
|
||||||
import { css } from 'emotion';
|
import { css } from 'emotion';
|
||||||
|
import { QueryGroupOptions } from 'app/types';
|
||||||
export interface QueryGroupOptions {
|
|
||||||
queries: DataQuery[];
|
|
||||||
dataSource: QueryGroupDataSource;
|
|
||||||
maxDataPoints?: number | null;
|
|
||||||
minInterval?: string | null;
|
|
||||||
cacheTimeout?: string | null;
|
|
||||||
timeRange?: {
|
|
||||||
from?: string | null;
|
|
||||||
shift?: string | null;
|
|
||||||
hide?: boolean;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface QueryGroupDataSource {
|
|
||||||
name?: string | null;
|
|
||||||
uid?: string;
|
|
||||||
default?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
options: QueryGroupOptions;
|
options: QueryGroupOptions;
|
||||||
|
@ -4,8 +4,8 @@ import { config } from 'app/core/config';
|
|||||||
import React, { FC, useMemo, useState } from 'react';
|
import React, { FC, useMemo, useState } from 'react';
|
||||||
import { useObservable } from 'react-use';
|
import { useObservable } from 'react-use';
|
||||||
import { QueryGroup } from '../query/components/QueryGroup';
|
import { QueryGroup } from '../query/components/QueryGroup';
|
||||||
import { QueryGroupOptions } from '../query/components/QueryGroupOptions';
|
|
||||||
import { PanelQueryRunner } from '../query/state/PanelQueryRunner';
|
import { PanelQueryRunner } from '../query/state/PanelQueryRunner';
|
||||||
|
import { QueryGroupOptions } from 'app/types';
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
queryRunner: PanelQueryRunner;
|
queryRunner: PanelQueryRunner;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { PanelData, SelectableValue } from '@grafana/data';
|
import { PanelData, SelectableValue } from '@grafana/data';
|
||||||
import { PanelQueryRunner } from '../features/query/state/PanelQueryRunner';
|
import { PanelQueryRunner } from '../features/query/state/PanelQueryRunner';
|
||||||
import { QueryGroupOptions } from '../features/query/components/QueryGroupOptions';
|
import { QueryGroupOptions } from './query';
|
||||||
|
|
||||||
export interface AlertRuleDTO {
|
export interface AlertRuleDTO {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -15,6 +15,7 @@ export * from './store';
|
|||||||
export * from './ldap';
|
export * from './ldap';
|
||||||
export * from './appEvent';
|
export * from './appEvent';
|
||||||
export * from './angular';
|
export * from './angular';
|
||||||
|
export * from './query';
|
||||||
|
|
||||||
import * as CoreEvents from './events';
|
import * as CoreEvents from './events';
|
||||||
export { CoreEvents };
|
export { CoreEvents };
|
||||||
|
20
public/app/types/query.ts
Normal file
20
public/app/types/query.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { DataQuery } from '@grafana/data';
|
||||||
|
|
||||||
|
export interface QueryGroupOptions {
|
||||||
|
queries: DataQuery[];
|
||||||
|
dataSource: QueryGroupDataSource;
|
||||||
|
maxDataPoints?: number | null;
|
||||||
|
minInterval?: string | null;
|
||||||
|
cacheTimeout?: string | null;
|
||||||
|
timeRange?: {
|
||||||
|
from?: string | null;
|
||||||
|
shift?: string | null;
|
||||||
|
hide?: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface QueryGroupDataSource {
|
||||||
|
name?: string | null;
|
||||||
|
uid?: string;
|
||||||
|
default?: boolean;
|
||||||
|
}
|
Reference in New Issue
Block a user