mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
AdhocFilters: Improve typing and signature of getTagKeys and getTagValues and behaviors (#74962)
* Add adhocFilters to DataQueryRequest * More changes * Progress * Working * added baseFilters to picker * Remove unused code * minor fix
This commit is contained in:
parent
695c1a08f3
commit
1105b93104
@ -414,9 +414,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "23"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "24"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "25"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "26"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "27"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "28"]
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "26"]
|
||||
],
|
||||
"packages/grafana-data/src/types/explore.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
@ -2869,17 +2867,6 @@ exports[`better eslint`] = {
|
||||
"public/app/features/variables/adhoc/actions.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"public/app/features/variables/adhoc/picker/AdHocFilter.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/features/variables/adhoc/picker/AdHocFilterBuilder.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/features/variables/adhoc/picker/AdHocFilterKey.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
|
||||
],
|
||||
"public/app/features/variables/adhoc/picker/AdHocFilterRenderer.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
@ -3956,9 +3943,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "25"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "26"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "27"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "28"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "29"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "30"]
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "28"]
|
||||
],
|
||||
"public/app/plugins/datasource/prometheus/language_provider.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
|
@ -14,7 +14,7 @@ import { DataQuery } from './query';
|
||||
import { RawTimeRange, TimeRange } from './time';
|
||||
import { CustomVariableSupport, DataSourceVariableSupport, StandardVariableSupport } from './variables';
|
||||
|
||||
import { DataSourceRef, WithAccessControlMetadata } from '.';
|
||||
import { AdHocVariableFilter, DataSourceRef, WithAccessControlMetadata } from '.';
|
||||
|
||||
export interface DataSourcePluginOptionsEditorProps<
|
||||
JSONData extends DataSourceJsonData = DataSourceJsonData,
|
||||
@ -287,12 +287,12 @@ abstract class DataSourceApi<
|
||||
/**
|
||||
* Get tag keys for adhoc filters
|
||||
*/
|
||||
getTagKeys?(options?: any): Promise<MetricFindValue[]>;
|
||||
getTagKeys?(options?: DataSourceGetTagKeysOptions): Promise<MetricFindValue[]>;
|
||||
|
||||
/**
|
||||
* Get tag values for adhoc filters
|
||||
*/
|
||||
getTagValues?(options: any): Promise<MetricFindValue[]>;
|
||||
getTagValues?(options: DataSourceGetTagValuesOptions): Promise<MetricFindValue[]>;
|
||||
|
||||
/**
|
||||
* Set after constructor call, as the data source instance is the most common thing to pass around
|
||||
@ -370,6 +370,35 @@ abstract class DataSourceApi<
|
||||
getDefaultQuery?(app: CoreApp): Partial<TQuery>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options argument to DataSourceAPI.getTagKeys
|
||||
*/
|
||||
export interface DataSourceGetTagKeysOptions {
|
||||
/**
|
||||
* The other existing filters or base filters. New in v10.3
|
||||
*/
|
||||
filters: AdHocVariableFilter[];
|
||||
/**
|
||||
* Context time range. New in v10.3
|
||||
*/
|
||||
timeRange?: TimeRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options argument to DataSourceAPI.getTagValues
|
||||
*/
|
||||
export interface DataSourceGetTagValuesOptions {
|
||||
key: string;
|
||||
/**
|
||||
* The other existing filters or base filters. New in v10.3
|
||||
*/
|
||||
filters: AdHocVariableFilter[];
|
||||
/**
|
||||
* Context time range. New in v10.3
|
||||
*/
|
||||
timeRange?: TimeRange;
|
||||
}
|
||||
|
||||
export interface MetadataInspectorProps<
|
||||
DSType extends DataSourceApi<TQuery, TOptions>,
|
||||
TQuery extends DataQuery = DataQuery,
|
||||
|
@ -55,6 +55,10 @@ export interface AdHocVariableModel extends BaseVariableModel {
|
||||
type: 'adhoc';
|
||||
datasource: DataSourceRef | null;
|
||||
filters: AdHocVariableFilter[];
|
||||
/**
|
||||
* Filters that are always applied to the lookup of keys. Not shown in the AdhocFilterBuilder UI.
|
||||
*/
|
||||
baseFilters?: AdHocVariableFilter[];
|
||||
}
|
||||
|
||||
export interface VariableOption {
|
||||
|
@ -12,12 +12,10 @@ import { ConditionSegment } from './ConditionSegment';
|
||||
interface Props {
|
||||
datasource: DataSourceRef | null;
|
||||
filters: AdHocVariableFilter[];
|
||||
baseFilters?: AdHocVariableFilter[];
|
||||
addFilter: (filter: AdHocVariableFilter) => void;
|
||||
removeFilter: (index: number) => void;
|
||||
changeFilter: (index: number, newFilter: AdHocVariableFilter) => void;
|
||||
// Passes options to the datasources getTagKeys(options?: any) method
|
||||
// which is called to fetch the available filter key options in AdHocFilterKey.tsx
|
||||
getTagKeysOptions?: any;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
@ -60,13 +58,21 @@ export class AdHocFilter extends PureComponent<Props> {
|
||||
datasource={this.props.datasource!}
|
||||
appendBefore={filters.length > 0 ? <ConditionSegment label="AND" /> : null}
|
||||
onCompleted={this.appendFilterToVariable}
|
||||
getTagKeysOptions={this.props.getTagKeysOptions}
|
||||
allFilters={this.getAllFilters()}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
getAllFilters() {
|
||||
if (this.props.baseFilters) {
|
||||
return this.props.baseFilters.concat(this.props.filters);
|
||||
}
|
||||
|
||||
return this.props.filters;
|
||||
}
|
||||
|
||||
renderFilters(filters: AdHocVariableFilter[], disabled?: boolean) {
|
||||
if (filters.length === 0 && disabled) {
|
||||
return <Segment disabled={disabled} value="No filters" options={[]} onChange={() => {}} />;
|
||||
@ -91,7 +97,7 @@ export class AdHocFilter extends PureComponent<Props> {
|
||||
onKeyChange={this.onChange(index, 'key')}
|
||||
onOperatorChange={this.onChange(index, 'operator')}
|
||||
onValueChange={this.onChange(index, 'value')}
|
||||
getTagKeysOptions={this.props.getTagKeysOptions}
|
||||
allFilters={this.getAllFilters()}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
@ -11,10 +11,10 @@ interface Props {
|
||||
datasource: DataSourceRef;
|
||||
onCompleted: (filter: AdHocVariableFilter) => void;
|
||||
appendBefore?: React.ReactNode;
|
||||
getTagKeysOptions?: any;
|
||||
allFilters: AdHocVariableFilter[];
|
||||
}
|
||||
|
||||
export const AdHocFilterBuilder = ({ datasource, appendBefore, onCompleted, getTagKeysOptions }: Props) => {
|
||||
export const AdHocFilterBuilder = ({ datasource, appendBefore, onCompleted, allFilters }: Props) => {
|
||||
const [key, setKey] = useState<string | null>(null);
|
||||
const [operator, setOperator] = useState<string>('=');
|
||||
|
||||
@ -49,14 +49,7 @@ export const AdHocFilterBuilder = ({ datasource, appendBefore, onCompleted, getT
|
||||
);
|
||||
|
||||
if (key === null) {
|
||||
return (
|
||||
<AdHocFilterKey
|
||||
datasource={datasource}
|
||||
filterKey={key}
|
||||
onChange={onKeyChanged}
|
||||
getTagKeysOptions={getTagKeysOptions}
|
||||
/>
|
||||
);
|
||||
return <AdHocFilterKey datasource={datasource} filterKey={key} onChange={onKeyChanged} allFilters={allFilters} />;
|
||||
}
|
||||
|
||||
return (
|
||||
@ -69,7 +62,7 @@ export const AdHocFilterBuilder = ({ datasource, appendBefore, onCompleted, getT
|
||||
onKeyChange={onKeyChanged}
|
||||
onOperatorChange={onOperatorChanged}
|
||||
onValueChange={onValueChanged}
|
||||
getTagKeysOptions={getTagKeysOptions}
|
||||
allFilters={allFilters}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
|
||||
import { DataSourceRef, SelectableValue } from '@grafana/data';
|
||||
import { AdHocVariableFilter, DataSourceRef, SelectableValue } from '@grafana/data';
|
||||
import { Icon, SegmentAsync } from '@grafana/ui';
|
||||
|
||||
import { getDatasourceSrv } from '../../../plugins/datasource_srv';
|
||||
@ -9,14 +9,14 @@ interface Props {
|
||||
datasource: DataSourceRef;
|
||||
filterKey: string | null;
|
||||
onChange: (item: SelectableValue<string | null>) => void;
|
||||
getTagKeysOptions?: any;
|
||||
allFilters: AdHocVariableFilter[];
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const MIN_WIDTH = 90;
|
||||
export const AdHocFilterKey = ({ datasource, onChange, disabled, filterKey, getTagKeysOptions }: Props) => {
|
||||
const loadKeys = () => fetchFilterKeys(datasource, getTagKeysOptions);
|
||||
const loadKeysWithRemove = () => fetchFilterKeysWithRemove(datasource, getTagKeysOptions);
|
||||
export const AdHocFilterKey = ({ datasource, onChange, disabled, filterKey, allFilters }: Props) => {
|
||||
const loadKeys = () => fetchFilterKeys(datasource, filterKey, allFilters);
|
||||
const loadKeysWithRemove = () => fetchFilterKeysWithRemove(datasource, filterKey, allFilters);
|
||||
|
||||
if (filterKey === null) {
|
||||
return (
|
||||
@ -59,7 +59,8 @@ const plusSegment: ReactElement = (
|
||||
|
||||
const fetchFilterKeys = async (
|
||||
datasource: DataSourceRef,
|
||||
getTagKeysOptions?: any
|
||||
currentKey: string | null,
|
||||
allFilters: AdHocVariableFilter[]
|
||||
): Promise<Array<SelectableValue<string>>> => {
|
||||
const ds = await getDatasourceSrv().get(datasource);
|
||||
|
||||
@ -67,14 +68,16 @@ const fetchFilterKeys = async (
|
||||
return [];
|
||||
}
|
||||
|
||||
const metrics = await ds.getTagKeys(getTagKeysOptions);
|
||||
const otherFilters = allFilters.filter((f) => f.key !== currentKey);
|
||||
const metrics = await ds.getTagKeys({ filters: otherFilters });
|
||||
return metrics.map((m) => ({ label: m.text, value: m.text }));
|
||||
};
|
||||
|
||||
const fetchFilterKeysWithRemove = async (
|
||||
datasource: DataSourceRef,
|
||||
getTagKeysOptions?: any
|
||||
currentKey: string | null,
|
||||
allFilters: AdHocVariableFilter[]
|
||||
): Promise<Array<SelectableValue<string>>> => {
|
||||
const keys = await fetchFilterKeys(datasource, getTagKeysOptions);
|
||||
const keys = await fetchFilterKeys(datasource, currentKey, allFilters);
|
||||
return [REMOVE_VALUE, ...keys];
|
||||
};
|
||||
|
@ -10,6 +10,7 @@ import { OperatorSegment } from './OperatorSegment';
|
||||
interface Props {
|
||||
datasource: DataSourceRef;
|
||||
filter: AdHocVariableFilter;
|
||||
allFilters: AdHocVariableFilter[];
|
||||
onKeyChange: (item: SelectableValue<string | null>) => void;
|
||||
onOperatorChange: (item: SelectableValue<string>) => void;
|
||||
onValueChange: (item: SelectableValue<string>) => void;
|
||||
@ -25,7 +26,7 @@ export const AdHocFilterRenderer = ({
|
||||
onOperatorChange,
|
||||
onValueChange,
|
||||
placeHolder,
|
||||
getTagKeysOptions,
|
||||
allFilters,
|
||||
disabled,
|
||||
}: Props) => {
|
||||
return (
|
||||
@ -35,7 +36,7 @@ export const AdHocFilterRenderer = ({
|
||||
datasource={datasource}
|
||||
filterKey={key}
|
||||
onChange={onKeyChange}
|
||||
getTagKeysOptions={getTagKeysOptions}
|
||||
allFilters={allFilters}
|
||||
/>
|
||||
<div className="gf-form">
|
||||
<OperatorSegment disabled={disabled} value={operator} onChange={onOperatorChange} />
|
||||
@ -45,6 +46,7 @@ export const AdHocFilterRenderer = ({
|
||||
datasource={datasource}
|
||||
filterKey={key}
|
||||
filterValue={value}
|
||||
allFilters={allFilters}
|
||||
onChange={onValueChange}
|
||||
placeHolder={placeHolder}
|
||||
/>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
import { DataSourceRef, MetricFindValue, SelectableValue } from '@grafana/data';
|
||||
import { AdHocVariableFilter, DataSourceRef, MetricFindValue, SelectableValue } from '@grafana/data';
|
||||
import { SegmentAsync } from '@grafana/ui';
|
||||
|
||||
import { getDatasourceSrv } from '../../../plugins/datasource_srv';
|
||||
@ -12,10 +12,19 @@ interface Props {
|
||||
onChange: (item: SelectableValue<string>) => void;
|
||||
placeHolder?: string;
|
||||
disabled?: boolean;
|
||||
allFilters: AdHocVariableFilter[];
|
||||
}
|
||||
|
||||
export const AdHocFilterValue = ({ datasource, disabled, onChange, filterKey, filterValue, placeHolder }: Props) => {
|
||||
const loadValues = () => fetchFilterValues(datasource, filterKey);
|
||||
export const AdHocFilterValue = ({
|
||||
datasource,
|
||||
disabled,
|
||||
onChange,
|
||||
filterKey,
|
||||
filterValue,
|
||||
placeHolder,
|
||||
allFilters,
|
||||
}: Props) => {
|
||||
const loadValues = () => fetchFilterValues(datasource, filterKey, allFilters);
|
||||
|
||||
return (
|
||||
<div className="gf-form" data-testid="AdHocFilterValue-value-wrapper">
|
||||
@ -31,13 +40,19 @@ export const AdHocFilterValue = ({ datasource, disabled, onChange, filterKey, fi
|
||||
);
|
||||
};
|
||||
|
||||
const fetchFilterValues = async (datasource: DataSourceRef, key: string): Promise<Array<SelectableValue<string>>> => {
|
||||
const fetchFilterValues = async (
|
||||
datasource: DataSourceRef,
|
||||
key: string,
|
||||
allFilters: AdHocVariableFilter[]
|
||||
): Promise<Array<SelectableValue<string>>> => {
|
||||
const ds = await getDatasourceSrv().get(datasource);
|
||||
|
||||
if (!ds || !ds.getTagValues) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const metrics = await ds.getTagValues({ key });
|
||||
// Filter out the current filter key from the list of all filters
|
||||
const otherFilters = allFilters.filter((f) => f.key !== key);
|
||||
const metrics = await ds.getTagValues({ key, filters: otherFilters });
|
||||
return metrics.map((m: MetricFindValue) => ({ label: m.text, value: m.text }));
|
||||
};
|
||||
|
@ -42,12 +42,13 @@ export class AdHocPickerUnconnected extends PureComponent<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { filters, datasource } = this.props.variable;
|
||||
const { filters, datasource, baseFilters } = this.props.variable;
|
||||
|
||||
return (
|
||||
<AdHocFilter
|
||||
datasource={datasource}
|
||||
filters={filters}
|
||||
baseFilters={baseFilters}
|
||||
disabled={this.props.readOnly}
|
||||
addFilter={this.addFilter}
|
||||
removeFilter={this.removeFilter}
|
||||
|
@ -20,6 +20,8 @@ import {
|
||||
TIME_SERIES_VALUE_FIELD_NAME,
|
||||
TimeSeries,
|
||||
toDataFrame,
|
||||
DataSourceGetTagKeysOptions,
|
||||
DataSourceGetTagValuesOptions,
|
||||
} from '@grafana/data';
|
||||
import {
|
||||
BackendDataSourceResponse,
|
||||
@ -345,27 +347,29 @@ export default class InfluxDatasource extends DataSourceWithBackend<InfluxQuery,
|
||||
|
||||
// By implementing getTagKeys and getTagValues we add ad-hoc filters functionality
|
||||
// Used in public/app/features/variables/adhoc/picker/AdHocFilterKey.tsx::fetchFilterKeys
|
||||
getTagKeys(options?: InfluxQuery) {
|
||||
getTagKeys(options?: DataSourceGetTagKeysOptions) {
|
||||
const query = buildMetadataQuery({
|
||||
type: 'TAG_KEYS',
|
||||
templateService: this.templateSrv,
|
||||
database: this.database,
|
||||
measurement: options?.measurement ?? '',
|
||||
measurement: '',
|
||||
tags: [],
|
||||
});
|
||||
return this.metricFindQuery(query, options);
|
||||
|
||||
return this.metricFindQuery(query);
|
||||
}
|
||||
|
||||
getTagValues(options: InfluxQuery) {
|
||||
getTagValues(options: DataSourceGetTagValuesOptions) {
|
||||
const query = buildMetadataQuery({
|
||||
type: 'TAG_VALUES',
|
||||
templateService: this.templateSrv,
|
||||
database: this.database,
|
||||
withKey: options.key ?? '',
|
||||
measurement: options?.measurement ?? '',
|
||||
withKey: options.key,
|
||||
measurement: '',
|
||||
tags: [],
|
||||
});
|
||||
return this.metricFindQuery(query, options);
|
||||
|
||||
return this.metricFindQuery(query);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -91,7 +91,7 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource }: Props)
|
||||
const variables = datasource.getVariables().map((variable: string) => ({ label: variable, value: variable }));
|
||||
if (!metric) {
|
||||
// get all the labels
|
||||
datasource.getTagKeys().then((labelNames: Array<{ text: string }>) => {
|
||||
datasource.getTagKeys({ filters: [] }).then((labelNames: Array<{ text: string }>) => {
|
||||
const names = labelNames.map(({ text }) => ({ label: text, value: text }));
|
||||
setLabelOptions([...variables, ...names]);
|
||||
});
|
||||
|
@ -172,10 +172,14 @@ describe('PrometheusDatasource', () => {
|
||||
).rejects.toMatchObject({
|
||||
message: expect.stringMatching('Browser access'),
|
||||
});
|
||||
await expect(directDs.getTagKeys()).rejects.toMatchObject({
|
||||
message: expect.stringMatching('Browser access'),
|
||||
});
|
||||
await expect(directDs.getTagValues()).rejects.toMatchObject({
|
||||
|
||||
const errorMock = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
await directDs.getTagKeys({ filters: [] });
|
||||
// Language provider currently catches and just logs the error
|
||||
expect(errorMock).toHaveBeenCalledTimes(1);
|
||||
|
||||
await expect(directDs.getTagValues({ filters: [], key: 'A' })).rejects.toMatchObject({
|
||||
message: expect.stringMatching('Browser access'),
|
||||
});
|
||||
});
|
||||
|
@ -24,6 +24,9 @@ import {
|
||||
ScopedVars,
|
||||
TimeRange,
|
||||
renderLegendFormat,
|
||||
DataSourceGetTagKeysOptions,
|
||||
DataSourceGetTagValuesOptions,
|
||||
MetricFindValue,
|
||||
} from '@grafana/data';
|
||||
import {
|
||||
BackendDataSourceResponse,
|
||||
@ -55,7 +58,8 @@ import {
|
||||
} from './language_utils';
|
||||
import PrometheusMetricFindQuery from './metric_find_query';
|
||||
import { getInitHints, getQueryHints } from './query_hints';
|
||||
import { QueryEditorMode } from './querybuilder/shared/types';
|
||||
import { promQueryModeller } from './querybuilder/PromQueryModeller';
|
||||
import { QueryBuilderLabelFilter, QueryEditorMode } from './querybuilder/shared/types';
|
||||
import { CacheRequestInfo, defaultPrometheusQueryOverlapWindow, QueryCache } from './querycache/QueryCache';
|
||||
import { getOriginalMetricName, transform, transformV2 } from './result_transformer';
|
||||
import { trackQuery } from './tracking';
|
||||
@ -971,27 +975,50 @@ export class PrometheusDatasource
|
||||
// this is used to get label keys, a.k.a label names
|
||||
// it is used in metric_find_query.ts
|
||||
// and in Tempo here grafana/public/app/plugins/datasource/tempo/QueryEditor/ServiceGraphSection.tsx
|
||||
async getTagKeys(options?: { series: string[] }) {
|
||||
if (options?.series) {
|
||||
// Get tags for the provided series only
|
||||
const seriesLabels: Array<Record<string, string[]>> = await Promise.all(
|
||||
options.series.map((series: string) => this.languageProvider.fetchSeriesLabels(series))
|
||||
);
|
||||
// Combines tags from all options.series provided
|
||||
let tags: string[] = [];
|
||||
seriesLabels.map((value) => (tags = tags.concat(Object.keys(value))));
|
||||
const uniqueLabels = [...new Set(tags)];
|
||||
return uniqueLabels.map((value: any) => ({ text: value }));
|
||||
} else {
|
||||
// Get all tags
|
||||
const params = this.getTimeRangeParams();
|
||||
const result = await this.metadataRequest('/api/v1/labels', params);
|
||||
return result?.data?.data?.map((value: any) => ({ text: value })) ?? [];
|
||||
async getTagKeys(options: DataSourceGetTagKeysOptions): Promise<MetricFindValue[]> {
|
||||
if (!options || options.filters.length === 0) {
|
||||
await this.languageProvider.fetchLabels();
|
||||
return this.languageProvider.getLabelKeys().map((k) => ({ value: k, text: k }));
|
||||
}
|
||||
|
||||
const labelFilters: QueryBuilderLabelFilter[] = options.filters.map((f) => ({
|
||||
label: f.key,
|
||||
value: f.value,
|
||||
op: f.operator,
|
||||
}));
|
||||
const expr = promQueryModeller.renderLabels(labelFilters);
|
||||
|
||||
let labelsIndex: Record<string, string[]>;
|
||||
|
||||
if (this.hasLabelsMatchAPISupport()) {
|
||||
labelsIndex = await this.languageProvider.fetchSeriesLabelsMatch(expr);
|
||||
} else {
|
||||
labelsIndex = await this.languageProvider.fetchSeriesLabels(expr);
|
||||
}
|
||||
|
||||
// filter out already used labels
|
||||
return Object.keys(labelsIndex)
|
||||
.filter((labelName) => !options.filters.find((filter) => filter.key === labelName))
|
||||
.map((k) => ({ value: k, text: k }));
|
||||
}
|
||||
|
||||
// By implementing getTagKeys and getTagValues we add ad-hoc filters functionality
|
||||
async getTagValues(options: { key?: string } = {}) {
|
||||
async getTagValues(options: DataSourceGetTagValuesOptions) {
|
||||
const labelFilters: QueryBuilderLabelFilter[] = options.filters.map((f) => ({
|
||||
label: f.key,
|
||||
value: f.value,
|
||||
op: f.operator,
|
||||
}));
|
||||
|
||||
const expr = promQueryModeller.renderLabels(labelFilters);
|
||||
|
||||
if (this.hasLabelsMatchAPISupport()) {
|
||||
return (await this.languageProvider.fetchSeriesValuesWithMatch(options.key, expr)).map((v) => ({
|
||||
value: v,
|
||||
text: v,
|
||||
}));
|
||||
}
|
||||
|
||||
const params = this.getTimeRangeParams();
|
||||
const result = await this.metadataRequest(`/api/v1/label/${options.key}/values`, params);
|
||||
return result?.data?.data?.map((value: any) => ({ text: value })) ?? [];
|
||||
|
@ -46,7 +46,7 @@ export default class PrometheusMetricFindQuery {
|
||||
}
|
||||
|
||||
if (labelNamesQuery) {
|
||||
return this.datasource.getTagKeys();
|
||||
return this.datasource.getTagKeys({ filters: [] });
|
||||
}
|
||||
|
||||
const labelValuesQuery = this.query.match(labelValuesRegex);
|
||||
|
@ -22,7 +22,6 @@ export function ServiceGraphSection({
|
||||
onChange: (value: TempoQuery) => void;
|
||||
}) {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const dsState = useAsync(() => getDS(graphDatasourceUid), [graphDatasourceUid]);
|
||||
|
||||
// Check if service graph metrics are being collected. If not, displays a warning
|
||||
@ -30,10 +29,14 @@ export function ServiceGraphSection({
|
||||
useEffect(() => {
|
||||
async function fn(ds: PrometheusDatasource) {
|
||||
const keys = await ds.getTagKeys({
|
||||
series: [
|
||||
'traces_service_graph_request_server_seconds_sum',
|
||||
'traces_service_graph_request_total',
|
||||
'traces_service_graph_request_failed_total',
|
||||
filters: [
|
||||
{
|
||||
key: '__name__',
|
||||
operator: '=~',
|
||||
value:
|
||||
'traces_service_graph_request_server_seconds_sum|traces_service_graph_request_total|traces_service_graph_request_failed_total',
|
||||
condition: '',
|
||||
},
|
||||
],
|
||||
});
|
||||
setHasKeys(Boolean(keys.length));
|
||||
@ -61,6 +64,7 @@ export function ServiceGraphSection({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const filters = queryToFilter(query.serviceMapQuery || '');
|
||||
|
||||
return (
|
||||
@ -70,9 +74,14 @@ export function ServiceGraphSection({
|
||||
<AdHocFilter
|
||||
datasource={{ uid: graphDatasourceUid }}
|
||||
filters={filters}
|
||||
getTagKeysOptions={{
|
||||
series: ['traces_service_graph_request_total', 'traces_spanmetrics_calls_total'],
|
||||
}}
|
||||
baseFilters={[
|
||||
{
|
||||
key: '__name__',
|
||||
operator: '=~',
|
||||
value: 'traces_service_graph_request_total|traces_spanmetrics_calls_total',
|
||||
condition: '',
|
||||
},
|
||||
]}
|
||||
addFilter={(filter: AdHocVariableFilter) => {
|
||||
onChange({
|
||||
...query,
|
||||
|
Loading…
Reference in New Issue
Block a user