mirror of
https://github.com/grafana/grafana.git
synced 2024-12-02 05:29:42 -06:00
Tempo: Add kind to TraceQL intrinsics (#65111)
* Add kind to TraceQL intrinsics * Fix after merging main * Fix intrinsics array reference after merge * Dispatch error message when TraceQL autocomplete fails to retrieve tag values
This commit is contained in:
parent
d4715a6f04
commit
990b3c07ab
@ -13,8 +13,7 @@ import { RawQuery } from '../../prometheus/querybuilder/shared/RawQuery';
|
||||
import { TraceqlFilter } from '../dataquery.gen';
|
||||
import { TempoDatasource } from '../datasource';
|
||||
import { TempoQueryBuilderOptions } from '../traceql/TempoQueryBuilderOptions';
|
||||
import { CompletionProvider } from '../traceql/autocomplete';
|
||||
import { traceqlGrammar } from '../traceql/traceql';
|
||||
import { intrinsics, traceqlGrammar } from '../traceql/traceql';
|
||||
import { TempoQuery } from '../types';
|
||||
|
||||
import DurationInput from './DurationInput';
|
||||
@ -102,7 +101,7 @@ const TraceQLSearch = ({ datasource, query, onChange }: Props) => {
|
||||
// filter out tags that already exist in the static fields
|
||||
const staticTags = datasource.search?.filters?.map((f) => f.tag) || [];
|
||||
staticTags.push('duration');
|
||||
const filteredTags = [...CompletionProvider.intrinsics, ...tags].filter((t) => !staticTags.includes(t));
|
||||
const filteredTags = [...intrinsics, ...tags].filter((t) => !staticTags.includes(t));
|
||||
|
||||
// Dynamic filters are all filters that don't match the ID of a filter in the datasource configuration
|
||||
// The duration tag is a special case since its selector is hard-coded
|
||||
|
@ -3,7 +3,7 @@ import { startCase } from 'lodash';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
|
||||
import { TraceqlFilter, TraceqlSearchScope } from '../dataquery.gen';
|
||||
import { CompletionProvider } from '../traceql/autocomplete';
|
||||
import { intrinsics } from '../traceql/traceql';
|
||||
|
||||
export const generateQueryFromFilters = (filters: TraceqlFilter[]) => {
|
||||
return `{${filters
|
||||
@ -23,7 +23,7 @@ const valueHelper = (f: TraceqlFilter) => {
|
||||
};
|
||||
const scopeHelper = (f: TraceqlFilter) => {
|
||||
// Intrinsic fields don't have a scope
|
||||
if (CompletionProvider.intrinsics.find((t) => t === f.tag)) {
|
||||
if (intrinsics.find((t) => t === f.tag)) {
|
||||
return '';
|
||||
}
|
||||
return (
|
||||
|
@ -8,7 +8,7 @@ import TagsInput from '../SearchTraceQLEditor/TagsInput';
|
||||
import { replaceAt } from '../SearchTraceQLEditor/utils';
|
||||
import { TraceqlFilter, TraceqlSearchScope } from '../dataquery.gen';
|
||||
import { TempoDatasource } from '../datasource';
|
||||
import { CompletionProvider } from '../traceql/autocomplete';
|
||||
import { intrinsics } from '../traceql/traceql';
|
||||
import { TempoJsonData } from '../types';
|
||||
|
||||
interface Props extends DataSourcePluginOptionsEditorProps<TempoJsonData> {
|
||||
@ -94,7 +94,7 @@ export function TraceQLSearchTags({ options, onOptionsChange, datasource }: Prop
|
||||
filters={options.jsonData.search?.filters || []}
|
||||
datasource={datasource}
|
||||
setError={() => {}}
|
||||
tags={[...CompletionProvider.intrinsics, ...(tags || [])]}
|
||||
tags={[...intrinsics, ...(tags || [])]}
|
||||
isTagsLoading={loading}
|
||||
hideValues={true}
|
||||
/>
|
||||
|
@ -6,6 +6,7 @@ import TempoLanguageProvider from '../language_provider';
|
||||
import { TempoJsonData } from '../types';
|
||||
|
||||
import { CompletionProvider } from './autocomplete';
|
||||
import { intrinsics, scopes } from './traceql';
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
@ -19,8 +20,8 @@ describe('CompletionProvider', () => {
|
||||
{} as monacoTypes.Position
|
||||
);
|
||||
expect((result! as monacoTypes.languages.CompletionList).suggestions).toEqual([
|
||||
...CompletionProvider.scopes.map((s) => expect.objectContaining({ label: s, insertText: s })),
|
||||
...CompletionProvider.intrinsics.map((s) => expect.objectContaining({ label: s, insertText: s })),
|
||||
...scopes.map((s) => expect.objectContaining({ label: s, insertText: s })),
|
||||
...intrinsics.map((s) => expect.objectContaining({ label: s, insertText: s })),
|
||||
expect.objectContaining({ label: 'bar', insertText: '.bar' }),
|
||||
expect.objectContaining({ label: 'foo', insertText: '.foo' }),
|
||||
]);
|
||||
@ -116,8 +117,8 @@ describe('CompletionProvider', () => {
|
||||
{} as monacoTypes.Position
|
||||
);
|
||||
expect((result! as monacoTypes.languages.CompletionList).suggestions).toEqual([
|
||||
...CompletionProvider.scopes.map((s) => expect.objectContaining({ label: s, insertText: `{ ${s}` })),
|
||||
...CompletionProvider.intrinsics.map((s) => expect.objectContaining({ label: s, insertText: `{ ${s}` })),
|
||||
...scopes.map((s) => expect.objectContaining({ label: s, insertText: `{ ${s}` })),
|
||||
...intrinsics.map((s) => expect.objectContaining({ label: s, insertText: `{ ${s}` })),
|
||||
expect.objectContaining({ label: 'bar', insertText: '{ .bar' }),
|
||||
expect.objectContaining({ label: 'foo', insertText: '{ .foo' }),
|
||||
]);
|
||||
|
@ -1,8 +1,14 @@
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { isFetchError } from '@grafana/runtime';
|
||||
import type { Monaco, monacoTypes } from '@grafana/ui';
|
||||
|
||||
import { createErrorNotification } from '../../../../core/copy/appNotification';
|
||||
import { notifyApp } from '../../../../core/reducers/appNotification';
|
||||
import { dispatch } from '../../../../store/store';
|
||||
import TempoLanguageProvider from '../language_provider';
|
||||
|
||||
import { intrinsics, scopes } from './traceql';
|
||||
|
||||
interface Props {
|
||||
languageProvider: TempoLanguageProvider;
|
||||
}
|
||||
@ -21,9 +27,6 @@ export class CompletionProvider implements monacoTypes.languages.CompletionItemP
|
||||
}
|
||||
|
||||
triggerCharacters = ['{', '.', '[', '(', '=', '~', ' ', '"'];
|
||||
|
||||
static readonly intrinsics: string[] = ['duration', 'name', 'status'];
|
||||
static readonly scopes: string[] = ['resource', 'span'];
|
||||
static readonly operators: string[] = ['=', '-', '+', '<', '>', '>=', '<=', '=~'];
|
||||
static readonly logicalOps: string[] = ['&&', '||'];
|
||||
|
||||
@ -139,7 +142,17 @@ export class CompletionProvider implements monacoTypes.languages.CompletionItemP
|
||||
type: 'OPERATOR',
|
||||
}));
|
||||
case 'SPANSET_IN_VALUE':
|
||||
const tagValues = await this.getTagValues(situation.tagName);
|
||||
let tagValues;
|
||||
try {
|
||||
tagValues = await this.getTagValues(situation.tagName);
|
||||
} catch (error) {
|
||||
if (isFetchError(error)) {
|
||||
dispatch(notifyApp(createErrorNotification(error.data.error, new Error(error.data.message))));
|
||||
} else if (error instanceof Error) {
|
||||
dispatch(notifyApp(createErrorNotification('Error', error)));
|
||||
}
|
||||
}
|
||||
|
||||
const items: Completion[] = [];
|
||||
|
||||
const getInsertionText = (val: SelectableValue<string>): string => {
|
||||
@ -149,7 +162,7 @@ export class CompletionProvider implements monacoTypes.languages.CompletionItemP
|
||||
return val.type === 'string' ? `"${val.label}"` : val.label!;
|
||||
};
|
||||
|
||||
tagValues.forEach((val) => {
|
||||
tagValues?.forEach((val) => {
|
||||
if (val?.label) {
|
||||
items.push({
|
||||
label: val.label,
|
||||
@ -181,7 +194,7 @@ export class CompletionProvider implements monacoTypes.languages.CompletionItemP
|
||||
}
|
||||
|
||||
private getIntrinsicsCompletions(prepend?: string): Completion[] {
|
||||
return CompletionProvider.intrinsics.map((key) => ({
|
||||
return intrinsics.map((key) => ({
|
||||
label: key,
|
||||
insertText: (prepend || '') + key,
|
||||
type: 'KEYWORD',
|
||||
@ -189,7 +202,7 @@ export class CompletionProvider implements monacoTypes.languages.CompletionItemP
|
||||
}
|
||||
|
||||
private getScopesCompletions(prepend?: string): Completion[] {
|
||||
return CompletionProvider.scopes.map((key) => ({
|
||||
return scopes.map((key) => ({
|
||||
label: key,
|
||||
insertText: (prepend || '') + key,
|
||||
type: 'SCOPE',
|
||||
@ -242,7 +255,7 @@ export class CompletionProvider implements monacoTypes.languages.CompletionItemP
|
||||
if (!op) {
|
||||
// There's no operator so we check if the name is one of the known scopes
|
||||
// { resource.|
|
||||
if (CompletionProvider.scopes.filter((w) => w === nameMatched?.groups?.word) && nameMatched?.groups?.post_dot) {
|
||||
if (scopes.filter((w) => w === nameMatched?.groups?.word) && nameMatched?.groups?.post_dot) {
|
||||
return {
|
||||
type: 'SPANSET_IN_NAME_SCOPE',
|
||||
};
|
||||
|
@ -26,9 +26,9 @@ export const operators = ['=', '!=', '>', '<', '>=', '<=', '=~'];
|
||||
export const stringOperators = ['=', '!=', '=~'];
|
||||
export const numberOperators = ['=', '!=', '>', '<', '>=', '<='];
|
||||
|
||||
const intrinsics = ['duration', 'name', 'status', 'parent'];
|
||||
export const intrinsics = ['duration', 'kind', 'name', 'status'];
|
||||
|
||||
const scopes: string[] = ['resource', 'span'];
|
||||
export const scopes: string[] = ['resource', 'span'];
|
||||
|
||||
const keywords = intrinsics.concat(scopes);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user