Exemplars: always query exemplars (#31673)

* Exemplars: always query exemplars

* Update exemplar button to be an eye

* Add tooltip to eye button
pull/32025/head
Zoltán Bedi 4 years ago committed by GitHub
parent ec95049a95
commit bb8a703428
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 67
      public/app/plugins/datasource/prometheus/components/PromExemplarField.tsx
  2. 3
      public/app/plugins/datasource/prometheus/components/PromExploreExtraField.test.tsx
  3. 6
      public/app/plugins/datasource/prometheus/components/PromExploreExtraField.tsx
  4. 9
      public/app/plugins/datasource/prometheus/components/PromExploreQueryEditor.tsx
  5. 4
      public/app/plugins/datasource/prometheus/components/PromQueryEditor.tsx
  6. 7
      public/app/plugins/datasource/prometheus/components/__snapshots__/PromExploreQueryEditor.test.tsx.snap
  7. 10
      public/app/plugins/datasource/prometheus/components/__snapshots__/PromQueryEditor.test.tsx.snap
  8. 25
      public/app/plugins/datasource/prometheus/datasource.ts

@ -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…
Cancel
Save