mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Pyroscope: Preselect default profile type or app in the query editor dropdown (#70624)
This commit is contained in:
parent
a2dad8a636
commit
0259ae2630
@ -1,12 +1,11 @@
|
||||
import { defaults } from 'lodash';
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
import { CoreApp, QueryEditorProps, TimeRange } from '@grafana/data';
|
||||
import { ButtonCascader, CascaderOption } from '@grafana/ui';
|
||||
|
||||
import { defaultGrafanaPyroscope, defaultPhlareQueryType, GrafanaPyroscope } from '../dataquery.gen';
|
||||
import { PhlareDataSource } from '../datasource';
|
||||
import { normalizeQuery, PhlareDataSource } from '../datasource';
|
||||
import { BackendType, PhlareDataSourceOptions, ProfileTypeMessage, Query } from '../types';
|
||||
|
||||
import { EditorRow } from './EditorRow';
|
||||
@ -16,31 +15,18 @@ import { QueryOptions } from './QueryOptions';
|
||||
|
||||
export type Props = QueryEditorProps<PhlareDataSource, Query, PhlareDataSourceOptions>;
|
||||
|
||||
export const defaultQuery: Partial<GrafanaPyroscope> = {
|
||||
...defaultGrafanaPyroscope,
|
||||
queryType: defaultPhlareQueryType,
|
||||
};
|
||||
|
||||
export function QueryEditor(props: Props) {
|
||||
let query = normalizeQuery(props.query, props.app);
|
||||
const { onChange, onRunQuery, datasource, query, range, app } = props;
|
||||
|
||||
function handleRunQuery(value: string) {
|
||||
props.onChange({ ...props.query, labelSelector: value });
|
||||
props.onRunQuery();
|
||||
onChange({ ...query, labelSelector: value });
|
||||
onRunQuery();
|
||||
}
|
||||
|
||||
const { profileTypes, onProfileTypeChange, selectedProfileName } = useProfileTypes(
|
||||
props.datasource,
|
||||
props.query,
|
||||
props.onChange,
|
||||
props.datasource.backendType
|
||||
);
|
||||
const { labels, getLabelValues, onLabelSelectorChange } = useLabels(
|
||||
props.range,
|
||||
props.datasource,
|
||||
props.query,
|
||||
props.onChange
|
||||
);
|
||||
const { profileTypes, onProfileTypeChange, selectedProfileName } = useProfileTypes(datasource, query, onChange);
|
||||
const { labels, getLabelValues, onLabelSelectorChange } = useLabels(range, datasource, query, onChange);
|
||||
useNormalizeQuery(query, profileTypes, onChange, app);
|
||||
|
||||
const cascaderOptions = useCascaderOptions(profileTypes);
|
||||
|
||||
return (
|
||||
@ -64,6 +50,42 @@ export function QueryEditor(props: Props) {
|
||||
);
|
||||
}
|
||||
|
||||
function useNormalizeQuery(
|
||||
query: Query,
|
||||
profileTypes: ProfileTypeMessage[],
|
||||
onChange: (value: Query) => void,
|
||||
app?: CoreApp
|
||||
) {
|
||||
useEffect(() => {
|
||||
const normalizedQuery = normalizeQuery(query, app);
|
||||
// Query can be stored with some old type, or we can have query from different pyro datasource
|
||||
const selectedProfile = query.profileTypeId && profileTypes.find((p) => p.id === query.profileTypeId);
|
||||
if (profileTypes.length && !selectedProfile) {
|
||||
normalizedQuery.profileTypeId = defaultProfileType(profileTypes);
|
||||
}
|
||||
// Makes sure we don't have an infinite loop updates because the normalization creates a new object
|
||||
if (!deepEqual(query, normalizedQuery)) {
|
||||
onChange(normalizedQuery);
|
||||
}
|
||||
}, [app, query, profileTypes, onChange]);
|
||||
}
|
||||
|
||||
function defaultProfileType(profileTypes: ProfileTypeMessage[]): string {
|
||||
const cpuProfiles = profileTypes.filter((p) => p.id.indexOf('cpu') >= 0);
|
||||
if (cpuProfiles.length) {
|
||||
// Prefer cpu time profile if available instead of samples
|
||||
const cpuTimeProfile = cpuProfiles.find((p) => p.id.indexOf('samples') === -1);
|
||||
if (cpuTimeProfile) {
|
||||
return cpuTimeProfile.id;
|
||||
}
|
||||
// Fallback to first cpu profile type
|
||||
return cpuProfiles[0].id;
|
||||
}
|
||||
|
||||
// Fallback to first profile type from response data
|
||||
return profileTypes[0].id;
|
||||
}
|
||||
|
||||
function useLabels(
|
||||
range: TimeRange | undefined,
|
||||
datasource: PhlareDataSource,
|
||||
@ -138,12 +160,7 @@ function useCascaderOptions(profileTypes: ProfileTypeMessage[]) {
|
||||
}, [profileTypes]);
|
||||
}
|
||||
|
||||
function useProfileTypes(
|
||||
datasource: PhlareDataSource,
|
||||
query: Query,
|
||||
onChange: (value: Query) => void,
|
||||
backendType: BackendType = 'phlare'
|
||||
) {
|
||||
function useProfileTypes(datasource: PhlareDataSource, query: Query, onChange: (value: Query) => void) {
|
||||
const [profileTypes, setProfileTypes] = useState<ProfileTypeMessage[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -160,23 +177,20 @@ function useProfileTypes(
|
||||
}
|
||||
|
||||
const id = selectedOptions[selectedOptions.length - 1].value;
|
||||
|
||||
// Probably cannot happen but makes TS happy
|
||||
if (typeof id !== 'string') {
|
||||
throw new Error('id is not string');
|
||||
}
|
||||
|
||||
onChange({ ...query, profileTypeId: id });
|
||||
},
|
||||
[onChange, query]
|
||||
);
|
||||
|
||||
const selectedProfileName = useProfileName(profileTypes, query.profileTypeId, backendType);
|
||||
|
||||
const selectedProfileName = useProfileName(profileTypes, query.profileTypeId, datasource.backendType);
|
||||
return { profileTypes, onProfileTypeChange, selectedProfileName };
|
||||
}
|
||||
|
||||
function useProfileName(profileTypes: ProfileTypeMessage[], profileTypeId: string, backendType: BackendType) {
|
||||
function useProfileName(
|
||||
profileTypes: ProfileTypeMessage[],
|
||||
profileTypeId: string,
|
||||
backendType: BackendType = 'phlare'
|
||||
) {
|
||||
return useMemo(() => {
|
||||
if (!profileTypes) {
|
||||
return 'Loading';
|
||||
@ -192,13 +206,3 @@ function useProfileName(profileTypes: ProfileTypeMessage[], profileTypeId: strin
|
||||
return profile.label;
|
||||
}, [profileTypeId, profileTypes, backendType]);
|
||||
}
|
||||
|
||||
export function normalizeQuery(query: Query, app?: CoreApp | string) {
|
||||
let normalized = defaults(query, defaultQuery);
|
||||
if (app !== CoreApp.Explore && normalized.queryType === 'both') {
|
||||
// In dashboards and other places, we can't show both types of graphs at the same time.
|
||||
// This will also be a default when having 'both' query and adding it from explore to dashboard
|
||||
normalized.queryType = 'profile';
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import { Observable, of } from 'rxjs';
|
||||
|
||||
import {
|
||||
AbstractQuery,
|
||||
CoreApp,
|
||||
DataQueryRequest,
|
||||
DataQueryResponse,
|
||||
DataSourceInstanceSettings,
|
||||
@ -12,7 +13,7 @@ import { DataSourceWithBackend, getTemplateSrv, TemplateSrv } from '@grafana/run
|
||||
|
||||
import { extractLabelMatchers, toPromLikeExpr } from '../prometheus/language_utils';
|
||||
|
||||
import { normalizeQuery } from './QueryEditor/QueryEditor';
|
||||
import { defaultGrafanaPyroscope, defaultPhlareQueryType } from './dataquery.gen';
|
||||
import { PhlareDataSourceOptions, Query, ProfileTypeMessage, BackendType } from './types';
|
||||
|
||||
export class PhlareDataSource extends DataSourceWithBackend<Query, PhlareDataSourceOptions> {
|
||||
@ -101,6 +102,25 @@ export class PhlareDataSource extends DataSourceWithBackend<Query, PhlareDataSou
|
||||
labelMatchers: extractLabelMatchers(tokens),
|
||||
};
|
||||
}
|
||||
|
||||
getDefaultQuery(app: CoreApp): Partial<Query> {
|
||||
return defaultQuery;
|
||||
}
|
||||
}
|
||||
|
||||
export const defaultQuery: Partial<Query> = {
|
||||
...defaultGrafanaPyroscope,
|
||||
queryType: defaultPhlareQueryType,
|
||||
};
|
||||
|
||||
export function normalizeQuery(query: Query, app?: CoreApp | string) {
|
||||
let normalized = { ...query, ...defaultQuery };
|
||||
if (app !== CoreApp.Explore && normalized.queryType === 'both') {
|
||||
// In dashboards and other places, we can't show both types of graphs at the same time.
|
||||
// This will also be a default when having 'both' query and adding it from explore to dashboard
|
||||
normalized.queryType = 'profile';
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
const grammar: Grammar = {
|
||||
|
Loading…
Reference in New Issue
Block a user