mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Exemplars: always query exemplars (#31673)
* Exemplars: always query exemplars * Update exemplar button to be an eye * Add tooltip to eye button
This commit is contained in:
parent
ec95049a95
commit
bb8a703428
@ -1,21 +1,68 @@
|
||||
import { InlineField, InlineSwitch } from '@grafana/ui';
|
||||
import React from 'react';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { FetchError } from '@grafana/runtime';
|
||||
import { IconButton, InlineLabel, Tooltip, useStyles } from '@grafana/ui';
|
||||
import { css, cx } from 'emotion';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { PrometheusDatasource } from '../datasource';
|
||||
import { PromQuery } from '../types';
|
||||
|
||||
interface Props {
|
||||
query: PromQuery;
|
||||
onChange: (value: PromQuery) => void;
|
||||
datasource: PrometheusDatasource;
|
||||
}
|
||||
|
||||
const onExemplarsChange = ({ query, onChange }: Props) => (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const exemplar = e.target.checked;
|
||||
onChange({ ...query, exemplar });
|
||||
};
|
||||
|
||||
export function PromExemplarField(props: Props) {
|
||||
const [error, setError] = useState<FetchError>();
|
||||
const styles = useStyles(getStyles);
|
||||
|
||||
useEffect(() => {
|
||||
const subscription = props.datasource.exemplarErrors.subscribe((err) => {
|
||||
setError(err);
|
||||
});
|
||||
return () => {
|
||||
subscription.unsubscribe();
|
||||
};
|
||||
}, [props]);
|
||||
|
||||
const iconButtonStyles = cx(
|
||||
{
|
||||
[styles.activeIcon]: !!props.query.exemplar,
|
||||
},
|
||||
styles.eyeIcon
|
||||
);
|
||||
|
||||
return (
|
||||
<InlineField label="Exemplars" labelWidth="auto">
|
||||
<InlineSwitch label="Exemplars" value={!!props.query.exemplar} onChange={onExemplarsChange(props)} />
|
||||
</InlineField>
|
||||
<InlineLabel width="auto">
|
||||
<Tooltip content={!!error ? 'Exemplars are not supported in this version of prometheus.' : ''}>
|
||||
<div className={styles.iconWrapper}>
|
||||
Exemplars
|
||||
<IconButton
|
||||
name="eye"
|
||||
tooltip={!!props.query.exemplar ? 'Disable query with exemplars' : 'Enable query with exemplars'}
|
||||
disabled={!!error}
|
||||
className={iconButtonStyles}
|
||||
onClick={() => {
|
||||
props.onChange({ ...props.query, exemplar: !props.query.exemplar });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</InlineLabel>
|
||||
);
|
||||
}
|
||||
|
||||
function getStyles(theme: GrafanaTheme) {
|
||||
return {
|
||||
eyeIcon: css`
|
||||
margin-left: ${theme.spacing.md};
|
||||
`,
|
||||
activeIcon: css`
|
||||
color: ${theme.palette.blue95};
|
||||
`,
|
||||
iconWrapper: css`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`,
|
||||
};
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { PromExploreExtraFieldProps, PromExploreExtraField } from './PromExploreExtraField';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
const setup = (propOverrides?: PromExploreExtraFieldProps) => {
|
||||
const queryType = 'range';
|
||||
const stepValue = '1';
|
||||
const query = { exemplar: false };
|
||||
const datasource = { exemplarErrors: new Observable() };
|
||||
const onStepChange = jest.fn();
|
||||
const onQueryTypeChange = jest.fn();
|
||||
const onKeyDownFunc = jest.fn();
|
||||
@ -17,6 +19,7 @@ const setup = (propOverrides?: PromExploreExtraFieldProps) => {
|
||||
onStepChange,
|
||||
onQueryTypeChange,
|
||||
onKeyDownFunc,
|
||||
datasource,
|
||||
};
|
||||
|
||||
Object.assign(props, propOverrides);
|
||||
|
@ -6,6 +6,7 @@ import { css, cx } from 'emotion';
|
||||
import { InlineFormLabel, RadioButtonGroup } from '@grafana/ui';
|
||||
import { PromQuery } from '../types';
|
||||
import { PromExemplarField } from './PromExemplarField';
|
||||
import { PrometheusDatasource } from '../datasource';
|
||||
|
||||
export interface PromExploreExtraFieldProps {
|
||||
queryType: string;
|
||||
@ -15,10 +16,11 @@ export interface PromExploreExtraFieldProps {
|
||||
onKeyDownFunc: (e: React.KeyboardEvent<HTMLInputElement>) => void;
|
||||
onQueryTypeChange: (value: string) => void;
|
||||
onChange: (value: PromQuery) => void;
|
||||
datasource: PrometheusDatasource;
|
||||
}
|
||||
|
||||
export const PromExploreExtraField: React.FC<PromExploreExtraFieldProps> = memo(
|
||||
({ queryType, stepValue, query, onChange, onStepChange, onQueryTypeChange, onKeyDownFunc }) => {
|
||||
({ queryType, stepValue, query, onChange, onStepChange, onQueryTypeChange, onKeyDownFunc, datasource }) => {
|
||||
const rangeOptions = [
|
||||
{ value: 'range', label: 'Range', description: 'Run query over a range of time.' },
|
||||
{
|
||||
@ -75,7 +77,7 @@ export const PromExploreExtraField: React.FC<PromExploreExtraFieldProps> = memo(
|
||||
/>
|
||||
</div>
|
||||
|
||||
<PromExemplarField query={query} onChange={onChange} />
|
||||
<PromExemplarField query={query} onChange={onChange} datasource={datasource} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { memo, FC } from 'react';
|
||||
import React, { memo, FC, useEffect } from 'react';
|
||||
|
||||
// Types
|
||||
import { ExploreQueryFieldProps } from '@grafana/data';
|
||||
@ -14,6 +14,12 @@ export type Props = ExploreQueryFieldProps<PrometheusDatasource, PromQuery, Prom
|
||||
export const PromExploreQueryEditor: FC<Props> = (props: Props) => {
|
||||
const { range, query, data, datasource, history, onChange, onRunQuery } = props;
|
||||
|
||||
useEffect(() => {
|
||||
if (query.exemplar === undefined) {
|
||||
onChange({ ...query, exemplar: true });
|
||||
}
|
||||
}, [query]);
|
||||
|
||||
function onChangeQueryStep(value: string) {
|
||||
const { query, onChange } = props;
|
||||
const nextQuery = { ...query, interval: value };
|
||||
@ -65,6 +71,7 @@ export const PromExploreQueryEditor: FC<Props> = (props: Props) => {
|
||||
onKeyDownFunc={onReturnKeyDown}
|
||||
query={query}
|
||||
onChange={onChange}
|
||||
datasource={datasource}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
@ -41,7 +41,7 @@ export class PromQueryEditor extends PureComponent<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
// Use default query to prevent undefined input values
|
||||
const defaultQuery: Partial<PromQuery> = { expr: '', legendFormat: '', interval: '' };
|
||||
const defaultQuery: Partial<PromQuery> = { expr: '', legendFormat: '', interval: '', exemplar: true };
|
||||
const query = Object.assign({}, defaultQuery, props.query);
|
||||
this.query = query;
|
||||
// Query target properties that are fully controlled inputs
|
||||
@ -186,7 +186,7 @@ export class PromQueryEditor extends PureComponent<Props, State> {
|
||||
</InlineFormLabel>
|
||||
</div>
|
||||
|
||||
<PromExemplarField query={query} onChange={onChange} />
|
||||
<PromExemplarField query={this.query} onChange={onChange} datasource={this.props.datasource} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -4,6 +4,13 @@ exports[`PromExploreQueryEditor should render component 1`] = `
|
||||
<PromQueryField
|
||||
ExtraFieldElement={
|
||||
<Memo
|
||||
datasource={
|
||||
Object {
|
||||
"languageProvider": Object {
|
||||
"syntax": [Function],
|
||||
},
|
||||
}
|
||||
}
|
||||
onChange={[MockFunction]}
|
||||
onKeyDownFunc={[Function]}
|
||||
onQueryTypeChange={[Function]}
|
||||
|
@ -172,6 +172,7 @@ exports[`Render PromQueryEditor with basic options should render 1`] = `
|
||||
}
|
||||
query={
|
||||
Object {
|
||||
"exemplar": true,
|
||||
"expr": "",
|
||||
"interval": "",
|
||||
"legendFormat": "",
|
||||
@ -182,10 +183,19 @@ exports[`Render PromQueryEditor with basic options should render 1`] = `
|
||||
</FormLabel>
|
||||
</div>
|
||||
<PromExemplarField
|
||||
datasource={
|
||||
Object {
|
||||
"createQuery": [MockFunction],
|
||||
"getPrometheusTime": [MockFunction],
|
||||
}
|
||||
}
|
||||
onChange={[MockFunction]}
|
||||
query={
|
||||
Object {
|
||||
"exemplar": true,
|
||||
"expr": "",
|
||||
"interval": "",
|
||||
"legendFormat": "",
|
||||
"refId": "A",
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_sr
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import defaults from 'lodash/defaults';
|
||||
import LRU from 'lru-cache';
|
||||
import { forkJoin, merge, Observable, of, pipe, throwError } from 'rxjs';
|
||||
import { forkJoin, merge, Observable, of, pipe, Subject, throwError } from 'rxjs';
|
||||
import { catchError, filter, map, tap } from 'rxjs/operators';
|
||||
import addLabelToQuery from './add_label_to_query';
|
||||
import PrometheusLanguageProvider from './language_provider';
|
||||
@ -62,6 +62,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
|
||||
exemplarTraceIdDestinations: ExemplarTraceIdDestination[] | undefined;
|
||||
lookupsDisabled: boolean;
|
||||
customQueryParameters: any;
|
||||
exemplarErrors: Subject<FetchError> = new Subject();
|
||||
|
||||
constructor(
|
||||
instanceSettings: DataSourceInstanceSettings<PromOptions>,
|
||||
@ -308,7 +309,16 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
|
||||
}
|
||||
|
||||
if (query.exemplar) {
|
||||
return this.getExemplars(query).pipe(filterAndMapResponse);
|
||||
return this.getExemplars(query).pipe(
|
||||
catchError((err: FetchError) => {
|
||||
this.exemplarErrors.next(err);
|
||||
return of({
|
||||
data: [],
|
||||
state: LoadingState.Done,
|
||||
});
|
||||
}),
|
||||
filterAndMapResponse
|
||||
);
|
||||
}
|
||||
|
||||
return this.performTimeSeriesQuery(query, query.start, query.end).pipe(filterAndMapResponse);
|
||||
@ -346,7 +356,16 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
|
||||
}
|
||||
|
||||
if (query.exemplar) {
|
||||
return this.getExemplars(query).pipe(filterAndMapResponse);
|
||||
return this.getExemplars(query).pipe(
|
||||
catchError((err: FetchError) => {
|
||||
this.exemplarErrors.next(err);
|
||||
return of({
|
||||
data: [],
|
||||
state: LoadingState.Done,
|
||||
});
|
||||
}),
|
||||
filterAndMapResponse
|
||||
);
|
||||
}
|
||||
|
||||
return this.performTimeSeriesQuery(query, query.start, query.end).pipe(filterAndMapResponse);
|
||||
|
Loading…
Reference in New Issue
Block a user