diff --git a/public/app/plugins/datasource/loki/components/LokiContextUi.tsx b/public/app/plugins/datasource/loki/components/LokiContextUi.tsx index 6b7dda28026..0b03fd32316 100644 --- a/public/app/plugins/datasource/loki/components/LokiContextUi.tsx +++ b/public/app/plugins/datasource/loki/components/LokiContextUi.tsx @@ -3,6 +3,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useAsync } from 'react-use'; import { dateTime, GrafanaTheme2, LogRowModel, renderMarkdown, SelectableValue } from '@grafana/data'; +import { RawQuery } from '@grafana/experimental'; import { reportInteraction } from '@grafana/runtime'; import { Alert, @@ -20,7 +21,6 @@ import { useStyles2, } from '@grafana/ui'; -import { RawQuery } from '../../prometheus/querybuilder/shared/RawQuery'; import { LogContextProvider, LOKI_LOG_CONTEXT_PRESERVED_LABELS, @@ -312,7 +312,11 @@ export function LokiContextUi(props: LokiContextUiProps) {
{initialized ? ( <> - + diff --git a/public/app/plugins/datasource/loki/components/LokiQueryEditor.test.tsx b/public/app/plugins/datasource/loki/components/LokiQueryEditor.test.tsx index 4264c1d24fb..c2720a046bf 100644 --- a/public/app/plugins/datasource/loki/components/LokiQueryEditor.test.tsx +++ b/public/app/plugins/datasource/loki/components/LokiQueryEditor.test.tsx @@ -4,7 +4,7 @@ import { cloneDeep, defaultsDeep } from 'lodash'; import React from 'react'; import { CoreApp } from '@grafana/data'; -import { QueryEditorMode } from 'app/plugins/datasource/prometheus/querybuilder/shared/types'; +import { QueryEditorMode } from '@grafana/experimental'; import { createLokiDatasource } from '../__mocks__/datasource'; import { EXPLAIN_LABEL_FILTER_CONTENT } from '../querybuilder/components/LokiQueryBuilderExplained'; diff --git a/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx b/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx index 5b2eb4a5e89..d0c9f84529a 100644 --- a/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx +++ b/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx @@ -4,12 +4,16 @@ import { usePrevious } from 'react-use'; import { CoreApp, LoadingState } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; -import { EditorHeader, EditorRows, FlexItem } from '@grafana/experimental'; +import { + EditorHeader, + EditorRows, + FlexItem, + QueryEditorModeToggle, + QueryHeaderSwitch, + QueryEditorMode, +} from '@grafana/experimental'; import { config, reportInteraction } from '@grafana/runtime'; import { Button, ConfirmModal, Space, Stack } from '@grafana/ui'; -import { QueryEditorModeToggle } from 'app/plugins/datasource/prometheus/querybuilder/shared/QueryEditorModeToggle'; -import { QueryHeaderSwitch } from 'app/plugins/datasource/prometheus/querybuilder/shared/QueryHeaderSwitch'; -import { QueryEditorMode } from 'app/plugins/datasource/prometheus/querybuilder/shared/types'; import { LabelBrowserModal } from '../querybuilder/components/LabelBrowserModal'; import { LokiQueryBuilderContainer } from '../querybuilder/components/LokiQueryBuilderContainer'; diff --git a/public/app/plugins/datasource/loki/modifyQuery.ts b/public/app/plugins/datasource/loki/modifyQuery.ts index db28c7a4ba2..b18887b4c13 100644 --- a/public/app/plugins/datasource/loki/modifyQuery.ts +++ b/public/app/plugins/datasource/loki/modifyQuery.ts @@ -1,6 +1,7 @@ import { NodeType, SyntaxNode } from '@lezer/common'; import { sortBy } from 'lodash'; +import { QueryBuilderLabelFilter } from '@grafana/experimental'; import { Identifier, LabelFilter, @@ -22,8 +23,6 @@ import { Expr, } from '@grafana/lezer-logql'; -import { QueryBuilderLabelFilter } from '../prometheus/querybuilder/shared/types'; - import { unescapeLabelValue } from './languageUtils'; import { getNodePositionsFromQuery } from './queryUtils'; import { lokiQueryModeller as modeller } from './querybuilder/LokiQueryModeller'; diff --git a/public/app/plugins/datasource/loki/querybuilder/LokiQueryModeller.test.ts b/public/app/plugins/datasource/loki/querybuilder/LokiQueryModeller.test.ts index fa621ee8d6a..7c7b7ac23bc 100644 --- a/public/app/plugins/datasource/loki/querybuilder/LokiQueryModeller.test.ts +++ b/public/app/plugins/datasource/loki/querybuilder/LokiQueryModeller.test.ts @@ -254,7 +254,7 @@ describe('LokiQueryModeller', () => { operations: [], }; - const def = modeller.getOperationDef('sum')!; + const def = modeller.getOperationDefinition('sum')!; const result = def.addOperationHandler(def, query, modeller); expect(result.operations[0].id).toBe('rate'); expect(result.operations[1].id).toBe('sum'); @@ -266,7 +266,7 @@ describe('LokiQueryModeller', () => { operations: [{ id: LokiOperationId.Json, params: [] }], }; - const def = modeller.getOperationDef('sum')!; + const def = modeller.getOperationDefinition('sum')!; const result = def.addOperationHandler(def, query, modeller); expect(result.operations[0].id).toBe(LokiOperationId.Json); expect(result.operations[1].id).toBe('rate'); @@ -279,7 +279,7 @@ describe('LokiQueryModeller', () => { operations: [{ id: 'rate', params: [] }], }; - const def = modeller.getOperationDef(LokiOperationId.Json)!; + const def = modeller.getOperationDefinition(LokiOperationId.Json)!; const result = def.addOperationHandler(def, query, modeller); expect(result.operations[0].id).toBe(LokiOperationId.Json); expect(result.operations[1].id).toBe('rate'); @@ -291,7 +291,7 @@ describe('LokiQueryModeller', () => { operations: [{ id: LokiOperationId.LineContains, params: ['error'] }], }; - const def = modeller.getOperationDef(LokiOperationId.Json)!; + const def = modeller.getOperationDefinition(LokiOperationId.Json)!; const result = def.addOperationHandler(def, query, modeller); expect(result.operations[0].id).toBe(LokiOperationId.LineContains); expect(result.operations[1].id).toBe(LokiOperationId.Json); @@ -303,7 +303,7 @@ describe('LokiQueryModeller', () => { operations: [{ id: LokiOperationId.Json, params: [] }], }; - const def = modeller.getOperationDef(LokiOperationId.LineContains)!; + const def = modeller.getOperationDefinition(LokiOperationId.LineContains)!; const result = def.addOperationHandler(def, query, modeller); expect(result.operations[0].id).toBe(LokiOperationId.LineContains); expect(result.operations[1].id).toBe(LokiOperationId.Json); @@ -315,7 +315,7 @@ describe('LokiQueryModeller', () => { operations: [{ id: LokiOperationId.Rate, params: [] }], }; - const def = modeller.getOperationDef(LokiOperationId.Rate)!; + const def = modeller.getOperationDefinition(LokiOperationId.Rate)!; const result = def.addOperationHandler(def, query, modeller); expect(result.operations.length).toBe(1); }); @@ -329,7 +329,7 @@ describe('LokiQueryModeller', () => { ], }; - const def = modeller.getOperationDef(LokiOperationId.Unwrap)!; + const def = modeller.getOperationDefinition(LokiOperationId.Unwrap)!; const result = def.addOperationHandler(def, query, modeller); expect(result.operations[1].id).toBe(LokiOperationId.Unwrap); }); diff --git a/public/app/plugins/datasource/loki/querybuilder/LokiQueryModeller.ts b/public/app/plugins/datasource/loki/querybuilder/LokiQueryModeller.ts index 2a95304c736..703d4c50f4f 100644 --- a/public/app/plugins/datasource/loki/querybuilder/LokiQueryModeller.ts +++ b/public/app/plugins/datasource/loki/querybuilder/LokiQueryModeller.ts @@ -1,12 +1,17 @@ -import { LokiAndPromQueryModellerBase } from '../../prometheus/querybuilder/shared/LokiAndPromQueryModellerBase'; -import { QueryBuilderLabelFilter } from '../../prometheus/querybuilder/shared/types'; +import { + QueryModellerBase, + QueryBuilderLabelFilter, + VisualQuery, + QueryBuilderOperation, + VisualQueryBinary, +} from '@grafana/experimental'; -import { getOperationDefinitions } from './operations'; +import { operationDefinitions } from './operations'; import { LokiOperationId, LokiQueryPattern, LokiQueryPatternType, LokiVisualQueryOperationCategory } from './types'; -export class LokiQueryModeller extends LokiAndPromQueryModellerBase { +export class LokiQueryModeller extends QueryModellerBase { constructor() { - super(getOperationDefinitions); + super(operationDefinitions, ''); this.setOperationCategories([ LokiVisualQueryOperationCategory.Aggregations, @@ -18,12 +23,65 @@ export class LokiQueryModeller extends LokiAndPromQueryModellerBase { ]); } - renderLabels(labels: QueryBuilderLabelFilter[]) { + renderOperations(queryString: string, operations: QueryBuilderOperation[]): string { + for (const operation of operations) { + const def = this.operationsRegistry.getIfExists(operation.id); + if (!def) { + console.error(`Could not find operation ${operation.id} in the registry`); + continue; + } + queryString = def.renderer(operation, def, queryString); + } + return queryString; + } + + renderBinaryQueries(queryString: string, binaryQueries?: Array>) { + if (binaryQueries) { + for (const binQuery of binaryQueries) { + queryString = `${this.renderBinaryQuery(queryString, binQuery)}`; + } + } + return queryString; + } + + private renderBinaryQuery(leftOperand: string, binaryQuery: VisualQueryBinary) { + let result = leftOperand + ` ${binaryQuery.operator} `; + + if (binaryQuery.vectorMatches) { + result += `${binaryQuery.vectorMatchesType}(${binaryQuery.vectorMatches}) `; + } + + return result + this.renderQuery(binaryQuery.query, true); + } + + renderLabels(labels: QueryBuilderLabelFilter[]): string { if (labels.length === 0) { return '{}'; } - return super.renderLabels(labels); + let expr = '{'; + for (const filter of labels) { + if (expr !== '{') { + expr += ', '; + } + + expr += `${filter.label}${filter.op}"${filter.value}"`; + } + + return expr + `}`; + } + + renderQuery(query: VisualQuery, nested?: boolean): string { + let queryString = this.renderLabels(query.labels); + queryString = this.renderOperations(queryString, query.operations); + + if (!nested && this.hasBinaryOp(query) && Boolean(query.binaryQueries?.length)) { + queryString = `(${queryString})`; + } + + queryString = this.renderBinaryQueries(queryString, query.binaryQueries); + + return queryString; } getQueryPatterns(): LokiQueryPattern[] { diff --git a/public/app/plugins/datasource/loki/querybuilder/binaryScalarOperations.ts b/public/app/plugins/datasource/loki/querybuilder/binaryScalarOperations.ts index cd62b2e8f49..a80f803eeab 100644 --- a/public/app/plugins/datasource/loki/querybuilder/binaryScalarOperations.ts +++ b/public/app/plugins/datasource/loki/querybuilder/binaryScalarOperations.ts @@ -1,8 +1,8 @@ import { QueryBuilderOperation, - QueryBuilderOperationDef, + QueryBuilderOperationDefinition, QueryBuilderOperationParamDef, -} from '../../prometheus/querybuilder/shared/types'; +} from '@grafana/experimental'; import { defaultAddOperationHandler } from './operationUtils'; import { LokiOperationId, LokiVisualQueryOperationCategory } from './types'; @@ -78,7 +78,7 @@ export const binaryScalarDefs = [ // Not sure about this one. It could also be a more generic 'Simple math operation' where user specifies // both the operator and the operand in a single input -export const binaryScalarOperations: QueryBuilderOperationDef[] = binaryScalarDefs.map((opDef) => { +export const binaryScalarOperations: QueryBuilderOperationDefinition[] = binaryScalarDefs.map((opDef) => { const params: QueryBuilderOperationParamDef[] = [{ name: 'Value', type: 'number' }]; const defaultParams: any[] = [2]; if (opDef.comparison) { @@ -103,7 +103,11 @@ export const binaryScalarOperations: QueryBuilderOperationDef[] = binaryScalarDe }); function getSimpleBinaryRenderer(operator: string) { - return function binaryRenderer(model: QueryBuilderOperation, def: QueryBuilderOperationDef, innerExpr: string) { + return function binaryRenderer( + model: QueryBuilderOperation, + def: QueryBuilderOperationDefinition, + innerExpr: string + ) { let param = model.params[0]; let bool = ''; if (model.params.length === 2) { diff --git a/public/app/plugins/datasource/loki/querybuilder/components/LabelParamEditor.tsx b/public/app/plugins/datasource/loki/querybuilder/components/LabelParamEditor.tsx new file mode 100644 index 00000000000..ea9186ebdd7 --- /dev/null +++ b/public/app/plugins/datasource/loki/querybuilder/components/LabelParamEditor.tsx @@ -0,0 +1,68 @@ +import React, { useState } from 'react'; + +import { DataSourceApi, SelectableValue } from '@grafana/data'; +import { + QueryBuilderLabelFilter, + QueryBuilderOperationParamEditorProps, + QueryBuilderOperationParamValue, + VisualQuery, + VisualQueryModeller, +} from '@grafana/experimental'; +import { Select } from '@grafana/ui'; + +import { getOperationParamId } from '../operationUtils'; + +export const LabelParamEditor = ({ + onChange, + index, + operationId, + value, + query, + datasource, + queryModeller, +}: QueryBuilderOperationParamEditorProps) => { + const [state, setState] = useState<{ + options?: SelectableValue[]; + isLoading?: boolean; + }>({}); + + return ( + + inputId={getOperationParamId(operationId, index)} + autoFocus={value === ''} + openMenuOnFocus + onOpenMenu={async () => { + setState({ isLoading: true }); + const options = await loadGroupByLabels(query, datasource, queryModeller); + setState({ options, isLoading: undefined }); + }} + isLoading={state.isLoading} + allowCustomValue + noOptionsMessage="No labels found" + loadingMessage="Loading labels" + options={state.options} + value={toOption(value)} + onChange={(value) => onChange(index, value.value!)} + /> + ); +}; + +async function loadGroupByLabels( + query: VisualQuery, + datasource: DataSourceApi, + queryModeller: VisualQueryModeller +): Promise { + let labels: QueryBuilderLabelFilter[] = query.labels; + + const queryString = queryModeller.renderLabels(labels); + const result = await datasource.languageProvider.fetchSeriesLabels(queryString); + + return Object.keys(result).map((x) => ({ + label: x, + value: x, + })); +} + +const toOption = ( + value: QueryBuilderOperationParamValue | undefined +): SelectableValue => ({ label: value?.toString(), value }); diff --git a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.test.tsx b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.test.tsx index 64347e14028..ba1d628a2d2 100644 --- a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.test.tsx +++ b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.test.tsx @@ -5,13 +5,13 @@ import { getSelectParent } from 'test/helpers/selectOptionInTest'; import { dateTime } from '@grafana/data'; -import { MISSING_LABEL_FILTER_ERROR_MESSAGE } from '../../../prometheus/querybuilder/shared/LabelFilters'; import { createLokiDatasource } from '../../__mocks__/datasource'; import { LokiOperationId, LokiVisualQuery } from '../types'; import { LokiQueryBuilder } from './LokiQueryBuilder'; import { EXPLAIN_LABEL_FILTER_CONTENT } from './LokiQueryBuilderExplained'; +const MISSING_LABEL_FILTER_ERROR_MESSAGE = 'Select at least 1 label filter (label and value)'; const defaultQuery: LokiVisualQuery = { labels: [{ op: '=', label: 'baz', value: 'bar' }], operations: [], diff --git a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.tsx b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.tsx index ad0e6c4c814..6c10532c5d0 100644 --- a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.tsx +++ b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.tsx @@ -1,25 +1,28 @@ import React, { useEffect, useMemo, useState } from 'react'; import { DataSourceApi, getDefaultTimeRange, LoadingState, PanelData, SelectableValue, TimeRange } from '@grafana/data'; -import { EditorRow } from '@grafana/experimental'; -import { config } from '@grafana/runtime'; -import { LabelFilters } from 'app/plugins/datasource/prometheus/querybuilder/shared/LabelFilters'; -import { OperationExplainedBox } from 'app/plugins/datasource/prometheus/querybuilder/shared/OperationExplainedBox'; -import { OperationList } from 'app/plugins/datasource/prometheus/querybuilder/shared/OperationList'; -import { OperationListExplained } from 'app/plugins/datasource/prometheus/querybuilder/shared/OperationListExplained'; -import { OperationsEditorRow } from 'app/plugins/datasource/prometheus/querybuilder/shared/OperationsEditorRow'; -import { QueryBuilderHints } from 'app/plugins/datasource/prometheus/querybuilder/shared/QueryBuilderHints'; -import { RawQuery } from 'app/plugins/datasource/prometheus/querybuilder/shared/RawQuery'; import { + EditorRow, + // LabelFilters, this is broken in @grafana/experimental so we need to use the one from prometheus + OperationExplainedBox, + OperationList, + OperationListExplained, + OperationsEditorRow, + QueryBuilderHints, + RawQuery, QueryBuilderLabelFilter, QueryBuilderOperation, -} from 'app/plugins/datasource/prometheus/querybuilder/shared/types'; +} from '@grafana/experimental'; +import { config } from '@grafana/runtime'; +import { LabelFilters } from 'app/plugins/datasource/prometheus/querybuilder/shared/LabelFilters'; import { testIds } from '../../components/LokiQueryEditor'; import { LokiDatasource } from '../../datasource'; import { escapeLabelValueInSelector } from '../../languageUtils'; import logqlGrammar from '../../syntax'; +import { LokiQuery } from '../../types'; import { lokiQueryModeller } from '../LokiQueryModeller'; +import { isConflictingFilter } from '../operationUtils'; import { buildVisualQueryFromString } from '../parsing'; import { LokiOperationId, LokiVisualQuery } from '../types'; @@ -130,7 +133,7 @@ export const LokiQueryBuilder = React.memo( {showExplain && ( } + title={} > {EXPLAIN_LABEL_FILTER_CONTENT} @@ -143,14 +146,19 @@ export const LokiQueryBuilder = React.memo( onRunQuery={onRunQuery} datasource={datasource as DataSourceApi} highlightedOp={highlightedOp} + isConflictingOperation={(operation: QueryBuilderOperation, otherOperations: QueryBuilderOperation[]) => + operation.id === LokiOperationId.LabelFilter && isConflictingFilter(operation, otherOperations) + } /> - + datasource={datasource} query={query} onChange={onChange} data={sampleData} queryModeller={lokiQueryModeller} buildVisualQueryFromString={buildVisualQueryFromString} + buildDataQueryFromQueryString={(queryString) => ({ expr: queryString, refId: 'hints' })} + buildQueryStringFromDataQuery={(query) => query.expr} /> {showExplain && ( @@ -158,7 +166,7 @@ export const LokiQueryBuilder = React.memo( stepNumber={2} queryModeller={lokiQueryModeller} query={query} - lang={lang} + language={lang} onMouseEnter={(op) => { setHighlightedOp(op); }} diff --git a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderExplained.tsx b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderExplained.tsx index ab5ee6552be..68430e8c2cb 100644 --- a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderExplained.tsx +++ b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderExplained.tsx @@ -1,9 +1,7 @@ import React from 'react'; +import { OperationExplainedBox, OperationListExplained, RawQuery } from '@grafana/experimental'; import { Stack } from '@grafana/ui'; -import { OperationExplainedBox } from 'app/plugins/datasource/prometheus/querybuilder/shared/OperationExplainedBox'; -import { OperationListExplained } from 'app/plugins/datasource/prometheus/querybuilder/shared/OperationListExplained'; -import { RawQuery } from 'app/plugins/datasource/prometheus/querybuilder/shared/RawQuery'; import { lokiGrammar } from '../../syntax'; import { lokiQueryModeller } from '../LokiQueryModeller'; @@ -24,7 +22,7 @@ export const LokiQueryBuilderExplained = React.memo(({ query }) => { } + title={} > {EXPLAIN_LABEL_FILTER_CONTENT} @@ -32,7 +30,7 @@ export const LokiQueryBuilderExplained = React.memo(({ query }) => { stepNumber={2} queryModeller={lokiQueryModeller} query={visQuery} - lang={lang} + language={lang} /> ); diff --git a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.tsx b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.tsx index 74934a42d10..ad9b7ae80f1 100644 --- a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.tsx +++ b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.tsx @@ -2,10 +2,9 @@ import { trim } from 'lodash'; import React, { useMemo, useState } from 'react'; import { CoreApp, isValidDuration, isValidGrafanaDuration, SelectableValue } from '@grafana/data'; -import { EditorField, EditorRow } from '@grafana/experimental'; +import { EditorField, EditorRow, QueryOptionGroup } from '@grafana/experimental'; import { config, reportInteraction } from '@grafana/runtime'; import { Alert, AutoSizeInput, RadioButtonGroup, Select } from '@grafana/ui'; -import { QueryOptionGroup } from 'app/plugins/datasource/prometheus/querybuilder/shared/QueryOptionGroup'; import { preprocessMaxLines, queryTypeOptions, RESOLUTION_OPTIONS } from '../../components/LokiOptionFields'; import { getLokiQueryType, isLogsQuery } from '../../queryUtils'; diff --git a/public/app/plugins/datasource/loki/querybuilder/components/QueryPattern.tsx b/public/app/plugins/datasource/loki/querybuilder/components/QueryPattern.tsx index a1b00f42842..c3d15c9711b 100644 --- a/public/app/plugins/datasource/loki/querybuilder/components/QueryPattern.tsx +++ b/public/app/plugins/datasource/loki/querybuilder/components/QueryPattern.tsx @@ -2,8 +2,8 @@ import { css } from '@emotion/css'; import React from 'react'; import { GrafanaTheme2 } from '@grafana/data'; +import { RawQuery } from '@grafana/experimental'; import { Button, Card, useStyles2 } from '@grafana/ui'; -import { RawQuery } from 'app/plugins/datasource/prometheus/querybuilder/shared/RawQuery'; import logqlGrammar from '../../syntax'; import { lokiQueryModeller } from '../LokiQueryModeller'; @@ -31,7 +31,7 @@ export const QueryPattern = (props: Props) => {
diff --git a/public/app/plugins/datasource/loki/querybuilder/components/QueryPreview.tsx b/public/app/plugins/datasource/loki/querybuilder/components/QueryPreview.tsx index ad64cdc3036..04fffba21ae 100644 --- a/public/app/plugins/datasource/loki/querybuilder/components/QueryPreview.tsx +++ b/public/app/plugins/datasource/loki/querybuilder/components/QueryPreview.tsx @@ -1,8 +1,7 @@ import React from 'react'; -import { EditorRow, EditorFieldGroup } from '@grafana/experimental'; +import { EditorRow, EditorFieldGroup, RawQuery } from '@grafana/experimental'; -import { RawQuery } from '../../../prometheus/querybuilder/shared/RawQuery'; import { lokiGrammar } from '../../syntax'; export interface Props { @@ -13,7 +12,7 @@ export function QueryPreview({ query }: Props) { return ( - + ); diff --git a/public/app/plugins/datasource/loki/querybuilder/components/UnwrapParamEditor.test.tsx b/public/app/plugins/datasource/loki/querybuilder/components/UnwrapParamEditor.test.tsx index 1ee163f7fc7..3c5f98e2cbe 100644 --- a/public/app/plugins/datasource/loki/querybuilder/components/UnwrapParamEditor.test.tsx +++ b/public/app/plugins/datasource/loki/querybuilder/components/UnwrapParamEditor.test.tsx @@ -3,14 +3,12 @@ import userEvent from '@testing-library/user-event'; import React, { ComponentProps } from 'react'; import { DataFrame, DataSourceApi, FieldType, toDataFrame } from '@grafana/data'; +import { QueryBuilderOperation, QueryBuilderOperationParamDef } from '@grafana/experimental'; import { config } from '@grafana/runtime'; -import { - QueryBuilderOperation, - QueryBuilderOperationParamDef, -} from 'app/plugins/datasource/prometheus/querybuilder/shared/types'; import { createLokiDatasource } from '../../__mocks__/datasource'; import { LokiDatasource } from '../../datasource'; +import { LokiQueryModeller } from '../LokiQueryModeller'; import { LokiOperationId } from '../types'; import { UnwrapParamEditor } from './UnwrapParamEditor'; @@ -96,6 +94,9 @@ const createProps = ( paramDef: {} as QueryBuilderOperationParamDef, operation: {} as QueryBuilderOperation, datasource: createLokiDatasource() as DataSourceApi, + queryModeller: { + renderQuery: jest.fn().mockReturnValue('sum_over_time({foo="bar"} | logfmt | unwrap [5m])'), + } as unknown as LokiQueryModeller, }; const props = { ...propsDefault, ...propsOverrides }; diff --git a/public/app/plugins/datasource/loki/querybuilder/components/UnwrapParamEditor.tsx b/public/app/plugins/datasource/loki/querybuilder/components/UnwrapParamEditor.tsx index f013a162fad..030b3235666 100644 --- a/public/app/plugins/datasource/loki/querybuilder/components/UnwrapParamEditor.tsx +++ b/public/app/plugins/datasource/loki/querybuilder/components/UnwrapParamEditor.tsx @@ -1,15 +1,14 @@ import React, { useState } from 'react'; import { SelectableValue, getDefaultTimeRange, toOption } from '@grafana/data'; +import { QueryBuilderOperationParamEditorProps, VisualQueryModeller } from '@grafana/experimental'; import { config } from '@grafana/runtime'; import { Select } from '@grafana/ui'; -import { QueryBuilderOperationParamEditorProps } from '../../../prometheus/querybuilder/shared/types'; import { placeHolderScopedVars } from '../../components/monaco-query-field/monaco-completion-provider/validation'; import { LokiDatasource } from '../../datasource'; import { getLogQueryFromMetricsQuery, isQueryWithError } from '../../queryUtils'; import { extractUnwrapLabelKeysFromDataFrame } from '../../responseUtils'; -import { lokiQueryModeller } from '../LokiQueryModeller'; import { getOperationParamId } from '../operationUtils'; import { LokiVisualQuery } from '../types'; @@ -21,6 +20,7 @@ export function UnwrapParamEditor({ query, datasource, timeRange, + queryModeller, }: QueryBuilderOperationParamEditorProps) { const [state, setState] = useState<{ options?: Array>; @@ -34,7 +34,7 @@ export function UnwrapParamEditor({ // This check is always true, we do it to make typescript happy if (datasource instanceof LokiDatasource && config.featureToggles.lokiQueryHints) { setState({ isLoading: true }); - const options = await loadUnwrapOptions(query, datasource, timeRange); + const options = await loadUnwrapOptions(query, datasource, queryModeller, timeRange); setState({ options, isLoading: undefined }); } }} @@ -56,9 +56,10 @@ export function UnwrapParamEditor({ async function loadUnwrapOptions( query: LokiVisualQuery, datasource: LokiDatasource, + queryModeller: VisualQueryModeller, timeRange = getDefaultTimeRange() ): Promise>> { - const queryExpr = lokiQueryModeller.renderQuery(query); + const queryExpr = queryModeller.renderQuery(query); const logExpr = getLogQueryFromMetricsQuery(queryExpr); if (isQueryWithError(datasource.interpolateString(logExpr, placeHolderScopedVars))) { return []; diff --git a/public/app/plugins/datasource/loki/querybuilder/operationUtils.test.ts b/public/app/plugins/datasource/loki/querybuilder/operationUtils.test.ts index 6c6bc4d3094..4ea7b5fed24 100644 --- a/public/app/plugins/datasource/loki/querybuilder/operationUtils.test.ts +++ b/public/app/plugins/datasource/loki/querybuilder/operationUtils.test.ts @@ -1,4 +1,4 @@ -import { QueryBuilderOperation, QueryBuilderOperationDef } from '../../prometheus/querybuilder/shared/types'; +import { QueryBuilderOperation, QueryBuilderOperationDefinition } from '@grafana/experimental'; import { createAggregationOperation, @@ -10,7 +10,7 @@ import { labelFilterRenderer, pipelineRenderer, } from './operationUtils'; -import { getOperationDefinitions } from './operations'; +import { operationDefinitions } from './operations'; import { LokiOperationId, LokiVisualQueryOperationCategory } from './types'; describe('createRangeOperation', () => { @@ -149,7 +149,7 @@ describe('getLineFilterRenderer', () => { params: ['`error`'], }; - const MOCK_DEF = undefined as unknown as QueryBuilderOperationDef; + const MOCK_DEF = undefined as unknown as QueryBuilderOperationDefinition; const MOCK_INNER_EXPR = '{job="grafana"}'; @@ -178,7 +178,7 @@ describe('getLineFilterRenderer', () => { describe('labelFilterRenderer', () => { const MOCK_MODEL = { id: '__label_filter', params: ['label', '', 'value'] }; - const MOCK_DEF = undefined as unknown as QueryBuilderOperationDef; + const MOCK_DEF = undefined as unknown as QueryBuilderOperationDefinition; const MOCK_INNER_EXPR = '{job="grafana"}'; it.each` @@ -220,17 +220,12 @@ describe('isConflictingFilter', () => { }); describe('pipelineRenderer', () => { - let definitions: QueryBuilderOperationDef[]; - beforeEach(() => { - definitions = getOperationDefinitions(); - }); - it('correctly renders unpack expressions', () => { const model: QueryBuilderOperation = { id: LokiOperationId.Unpack, params: [], }; - const definition = definitions.find((def) => def.id === LokiOperationId.Unpack); + const definition = operationDefinitions.find((def) => def.id === LokiOperationId.Unpack); expect(pipelineRenderer(model, definition!, '{}')).toBe('{} | unpack'); }); @@ -239,7 +234,7 @@ describe('pipelineRenderer', () => { id: LokiOperationId.Unpack, params: [], }; - const definition = definitions.find((def) => def.id === LokiOperationId.Unpack); + const definition = operationDefinitions.find((def) => def.id === LokiOperationId.Unpack); expect(pipelineRenderer(model, definition!, '{}')).toBe('{} | unpack'); }); @@ -248,7 +243,7 @@ describe('pipelineRenderer', () => { id: LokiOperationId.Logfmt, params: [], }; - const definition = definitions.find((def) => def.id === LokiOperationId.Logfmt); + const definition = operationDefinitions.find((def) => def.id === LokiOperationId.Logfmt); expect(pipelineRenderer(model, definition!, '{}')).toBe('{} | logfmt'); }); @@ -257,7 +252,7 @@ describe('pipelineRenderer', () => { id: LokiOperationId.Logfmt, params: [true, false, 'foo', ''], }; - const definition = definitions.find((def) => def.id === LokiOperationId.Logfmt); + const definition = operationDefinitions.find((def) => def.id === LokiOperationId.Logfmt); expect(pipelineRenderer(model, definition!, '{}')).toBe('{} | logfmt --strict foo'); }); @@ -266,7 +261,7 @@ describe('pipelineRenderer', () => { id: LokiOperationId.Logfmt, params: [true, false, 'foo', 'bar', 'baz'], }; - const definition = definitions.find((def) => def.id === LokiOperationId.Logfmt); + const definition = operationDefinitions.find((def) => def.id === LokiOperationId.Logfmt); expect(pipelineRenderer(model, definition!, '{}')).toBe('{} | logfmt --strict foo, bar, baz'); }); @@ -275,7 +270,7 @@ describe('pipelineRenderer', () => { id: LokiOperationId.Json, params: [], }; - const definition = definitions.find((def) => def.id === LokiOperationId.Json); + const definition = operationDefinitions.find((def) => def.id === LokiOperationId.Json); expect(pipelineRenderer(model, definition!, '{}')).toBe('{} | json'); }); @@ -284,7 +279,7 @@ describe('pipelineRenderer', () => { id: LokiOperationId.Json, params: ['foo', ''], }; - const definition = definitions.find((def) => def.id === LokiOperationId.Json); + const definition = operationDefinitions.find((def) => def.id === LokiOperationId.Json); expect(pipelineRenderer(model, definition!, '{}')).toBe('{} | json foo'); }); @@ -293,7 +288,7 @@ describe('pipelineRenderer', () => { id: LokiOperationId.Json, params: ['foo', 'bar', 'baz'], }; - const definition = definitions.find((def) => def.id === LokiOperationId.Json); + const definition = operationDefinitions.find((def) => def.id === LokiOperationId.Json); expect(pipelineRenderer(model, definition!, '{}')).toBe('{} | json foo, bar, baz'); }); @@ -302,7 +297,7 @@ describe('pipelineRenderer', () => { id: LokiOperationId.Keep, params: ['foo', ''], }; - const definition = definitions.find((def) => def.id === LokiOperationId.Keep); + const definition = operationDefinitions.find((def) => def.id === LokiOperationId.Keep); expect(pipelineRenderer(model, definition!, '{}')).toBe('{} | keep foo'); }); @@ -311,7 +306,7 @@ describe('pipelineRenderer', () => { id: LokiOperationId.Keep, params: ['foo', 'bar', 'baz'], }; - const definition = definitions.find((def) => def.id === LokiOperationId.Keep); + const definition = operationDefinitions.find((def) => def.id === LokiOperationId.Keep); expect(pipelineRenderer(model, definition!, '{}')).toBe('{} | keep foo, bar, baz'); }); @@ -320,7 +315,7 @@ describe('pipelineRenderer', () => { id: LokiOperationId.Drop, params: ['foo', ''], }; - const definition = definitions.find((def) => def.id === LokiOperationId.Drop); + const definition = operationDefinitions.find((def) => def.id === LokiOperationId.Drop); expect(pipelineRenderer(model, definition!, '{}')).toBe('{} | drop foo'); }); @@ -329,7 +324,7 @@ describe('pipelineRenderer', () => { id: LokiOperationId.Drop, params: ['foo', 'bar', 'baz'], }; - const definition = definitions.find((def) => def.id === LokiOperationId.Drop); + const definition = operationDefinitions.find((def) => def.id === LokiOperationId.Drop); expect(pipelineRenderer(model, definition!, '{}')).toBe('{} | drop foo, bar, baz'); }); }); diff --git a/public/app/plugins/datasource/loki/querybuilder/operationUtils.ts b/public/app/plugins/datasource/loki/querybuilder/operationUtils.ts index 14b06912617..971ae7e3fc0 100644 --- a/public/app/plugins/datasource/loki/querybuilder/operationUtils.ts +++ b/public/app/plugins/datasource/loki/querybuilder/operationUtils.ts @@ -1,21 +1,25 @@ import { capitalize } from 'lodash'; import pluralize from 'pluralize'; -import { LabelParamEditor } from '../../prometheus/querybuilder/components/LabelParamEditor'; import { QueryBuilderOperation, - QueryBuilderOperationDef, + QueryBuilderOperationDefinition, QueryBuilderOperationParamDef, QueryBuilderOperationParamValue, - QueryWithOperations, + VisualQuery, VisualQueryModeller, -} from '../../prometheus/querybuilder/shared/types'; +} from '@grafana/experimental'; + import { escapeLabelValueInExactSelector } from '../languageUtils'; import { FUNCTIONS } from '../syntax'; +import { LabelParamEditor } from './components/LabelParamEditor'; import { LokiOperationId, LokiOperationOrder, LokiVisualQuery, LokiVisualQueryOperationCategory } from './types'; -export function createRangeOperation(name: string, isRangeOperationWithGrouping?: boolean): QueryBuilderOperationDef { +export function createRangeOperation( + name: string, + isRangeOperationWithGrouping?: boolean +): QueryBuilderOperationDefinition { const params = [getRangeVectorParamDef()]; const defaultParams = ['$__auto']; let paramChangedHandler = undefined; @@ -62,11 +66,11 @@ export function createRangeOperation(name: string, isRangeOperationWithGrouping? }; } -export function createRangeOperationWithGrouping(name: string): QueryBuilderOperationDef[] { +export function createRangeOperationWithGrouping(name: string): QueryBuilderOperationDefinition[] { const rangeOperation = createRangeOperation(name, true); // Copy range operation params without the last param const params = rangeOperation.params.slice(0, -1); - const operations: QueryBuilderOperationDef[] = [ + const operations: QueryBuilderOperationDefinition[] = [ rangeOperation, { id: `__${name}_by`, @@ -118,7 +122,11 @@ export function createRangeOperationWithGrouping(name: string): QueryBuilderOper } export function getRangeAggregationWithGroupingRenderer(aggregation: string, grouping: 'by' | 'without') { - return function aggregationRenderer(model: QueryBuilderOperation, def: QueryBuilderOperationDef, innerExpr: string) { + return function aggregationRenderer( + model: QueryBuilderOperation, + def: QueryBuilderOperationDefinition, + innerExpr: string + ) { const restParamIndex = def.params.findIndex((param) => param.restParam); const params = model.params.slice(0, restParamIndex); const restParams = model.params.slice(restParamIndex); @@ -133,7 +141,7 @@ export function getRangeAggregationWithGroupingRenderer(aggregation: string, gro function operationWithRangeVectorRenderer( model: QueryBuilderOperation, - def: QueryBuilderOperationDef, + def: QueryBuilderOperationDefinition, innerExpr: string ) { const params = model.params ?? []; @@ -147,7 +155,11 @@ function operationWithRangeVectorRenderer( return `${model.id}(${innerExpr} [${params[0] ?? '$__auto'}])`; } -export function labelFilterRenderer(model: QueryBuilderOperation, def: QueryBuilderOperationDef, innerExpr: string) { +export function labelFilterRenderer( + model: QueryBuilderOperation, + def: QueryBuilderOperationDefinition, + innerExpr: string +) { const integerOperators = ['<', '<=', '>', '>=']; if (integerOperators.includes(String(model.params[1]))) { @@ -161,6 +173,9 @@ export function isConflictingFilter( operation: QueryBuilderOperation, queryOperations: QueryBuilderOperation[] ): boolean { + if (!operation) { + return false; + } const operationIsNegative = operation.params[1].toString().startsWith('!'); const candidates = queryOperations.filter( @@ -183,7 +198,11 @@ export function isConflictingFilter( return conflict; } -export function pipelineRenderer(model: QueryBuilderOperation, def: QueryBuilderOperationDef, innerExpr: string) { +export function pipelineRenderer( + model: QueryBuilderOperation, + def: QueryBuilderOperationDefinition, + innerExpr: string +) { switch (model.id) { case LokiOperationId.Logfmt: const [strict = false, keepEmpty = false, ...labels] = model.params; @@ -201,17 +220,17 @@ export function pipelineRenderer(model: QueryBuilderOperation, def: QueryBuilder } } -function isRangeVectorFunction(def: QueryBuilderOperationDef) { +function isRangeVectorFunction(def: QueryBuilderOperationDefinition) { return def.category === LokiVisualQueryOperationCategory.RangeFunctions; } function getIndexOfOrLast( operations: QueryBuilderOperation[], queryModeller: VisualQueryModeller, - condition: (def: QueryBuilderOperationDef) => boolean + condition: (def: QueryBuilderOperationDefinition) => boolean ) { const index = operations.findIndex((x) => { - const opDef = queryModeller.getOperationDef(x.id); + const opDef = queryModeller.getOperationDefinition(x.id); if (!opDef) { return false; } @@ -222,7 +241,7 @@ function getIndexOfOrLast( } export function addLokiOperation( - def: QueryBuilderOperationDef, + def: QueryBuilderOperationDefinition, query: LokiVisualQuery, modeller: VisualQueryModeller ): LokiVisualQuery { @@ -234,7 +253,7 @@ export function addLokiOperation( const operations = [...query.operations]; const existingRangeVectorFunction = operations.find((x) => { - const opDef = modeller.getOperationDef(x.id); + const opDef = modeller.getOperationDefinition(x.id); if (!opDef) { return false; } @@ -280,7 +299,7 @@ export function addLokiOperation( }; } -export function addNestedQueryHandler(def: QueryBuilderOperationDef, query: LokiVisualQuery): LokiVisualQuery { +export function addNestedQueryHandler(def: QueryBuilderOperationDefinition, query: LokiVisualQuery): LokiVisualQuery { return { ...query, binaryQueries: [ @@ -294,7 +313,11 @@ export function addNestedQueryHandler(def: QueryBuilderOperationDef, query: Loki } export function getLineFilterRenderer(operation: string, caseInsensitive?: boolean) { - return function lineFilterRenderer(model: QueryBuilderOperation, def: QueryBuilderOperationDef, innerExpr: string) { + return function lineFilterRenderer( + model: QueryBuilderOperation, + def: QueryBuilderOperationDefinition, + innerExpr: string + ) { const hasBackticks = model.params.some((param) => typeof param === 'string' && param.includes('`')); const delimiter = hasBackticks ? '"' : '`'; let params; @@ -324,7 +347,7 @@ export function getOperationParamId(operationId: string, paramIndex: number) { } export function getOnLabelAddedHandler(changeToOperationId: string) { - return function onParamChanged(index: number, op: QueryBuilderOperation, def: QueryBuilderOperationDef) { + return function onParamChanged(index: number, op: QueryBuilderOperation, def: QueryBuilderOperationDefinition) { // Check if we actually have the label param. As it's optional the aggregation can have one less, which is the // case of just simple aggregation without label. When user adds the label it now has the same number of params // as its definition, and now we can change it to its `_by` variant. @@ -361,7 +384,7 @@ export function getAggregationExplainer(aggregationName: string, mode: 'by' | 'w * This function will transform operations without labels to their plan aggregation operation */ export function getLastLabelRemovedHandler(changeToOperationId: string) { - return function onParamChanged(index: number, op: QueryBuilderOperation, def: QueryBuilderOperationDef) { + return function onParamChanged(index: number, op: QueryBuilderOperation, def: QueryBuilderOperationDefinition) { // If definition has more params then is defined there are no optional rest params anymore. // We then transform this operation into a different one if (op.params.length < def.params.length) { @@ -379,7 +402,7 @@ export function getLokiOperationDisplayName(funcName: string) { return capitalize(funcName.replace(/_/g, ' ')); } -export function defaultAddOperationHandler(def: QueryBuilderOperationDef, query: T) { +export function defaultAddOperationHandler(def: QueryBuilderOperationDefinition, query: T) { const newOperation: QueryBuilderOperation = { id: def.id, params: def.defaultParams, @@ -393,9 +416,9 @@ export function defaultAddOperationHandler(def: Q export function createAggregationOperation( name: string, - overrides: Partial = {} -): QueryBuilderOperationDef[] { - const operations: QueryBuilderOperationDef[] = [ + overrides: Partial = {} +): QueryBuilderOperationDefinition[] { + const operations: QueryBuilderOperationDefinition[] = [ { id: name, name: getLokiOperationDisplayName(name), @@ -466,12 +489,20 @@ export function createAggregationOperation( } function getAggregationWithoutRenderer(aggregation: string) { - return function aggregationRenderer(model: QueryBuilderOperation, def: QueryBuilderOperationDef, innerExpr: string) { + return function aggregationRenderer( + model: QueryBuilderOperation, + def: QueryBuilderOperationDefinition, + innerExpr: string + ) { return `${aggregation} without(${model.params.join(', ')}) (${innerExpr})`; }; } -export function functionRendererLeft(model: QueryBuilderOperation, def: QueryBuilderOperationDef, innerExpr: string) { +export function functionRendererLeft( + model: QueryBuilderOperation, + def: QueryBuilderOperationDefinition, + innerExpr: string +) { const params = renderParams(model, def, innerExpr); const str = model.id + '('; @@ -482,7 +513,7 @@ export function functionRendererLeft(model: QueryBuilderOperation, def: QueryBui return str + params.join(', ') + ')'; } -function renderParams(model: QueryBuilderOperation, def: QueryBuilderOperationDef, innerExpr: string) { +function renderParams(model: QueryBuilderOperation, def: QueryBuilderOperationDefinition, innerExpr: string) { return (model.params ?? []).map((value, index) => { const paramDef = def.params[index]; if (paramDef.type === 'string') { @@ -494,7 +525,11 @@ function renderParams(model: QueryBuilderOperation, def: QueryBuilderOperationDe } function getAggregationByRenderer(aggregation: string) { - return function aggregationRenderer(model: QueryBuilderOperation, def: QueryBuilderOperationDef, innerExpr: string) { + return function aggregationRenderer( + model: QueryBuilderOperation, + def: QueryBuilderOperationDefinition, + innerExpr: string + ) { return `${aggregation} by(${model.params.join(', ')}) (${innerExpr})`; }; } @@ -502,8 +537,8 @@ function getAggregationByRenderer(aggregation: string) { export function createAggregationOperationWithParam( name: string, paramsDef: { params: QueryBuilderOperationParamDef[]; defaultParams: QueryBuilderOperationParamValue[] }, - overrides: Partial = {} -): QueryBuilderOperationDef[] { + overrides: Partial = {} +): QueryBuilderOperationDefinition[] { const operations = createAggregationOperation(name, overrides); operations[0].params.unshift(...paramsDef.params); operations[1].params.unshift(...paramsDef.params); @@ -517,7 +552,11 @@ export function createAggregationOperationWithParam( } function getAggregationByRendererWithParameter(aggregation: string) { - return function aggregationRenderer(model: QueryBuilderOperation, def: QueryBuilderOperationDef, innerExpr: string) { + return function aggregationRenderer( + model: QueryBuilderOperation, + def: QueryBuilderOperationDefinition, + innerExpr: string + ) { const restParamIndex = def.params.findIndex((param) => param.restParam); const params = model.params.slice(0, restParamIndex); const restParams = model.params.slice(restParamIndex); diff --git a/public/app/plugins/datasource/loki/querybuilder/operations.test.ts b/public/app/plugins/datasource/loki/querybuilder/operations.test.ts index ccd1ee1254e..7d0a66741b2 100644 --- a/public/app/plugins/datasource/loki/querybuilder/operations.test.ts +++ b/public/app/plugins/datasource/loki/querybuilder/operations.test.ts @@ -1,4 +1,4 @@ -import { explainOperator, getOperationDefinitions } from './operations'; +import { explainOperator, operationDefinitions } from './operations'; import { LokiOperationId } from './types'; const undocumentedOperationsIds: string[] = [ @@ -21,8 +21,7 @@ describe('explainOperator', () => { let operations = []; let undocumentedOperations = []; - const definitions = getOperationDefinitions(); - for (const definition of definitions) { + for (const definition of operationDefinitions) { if (!undocumentedOperationsIds.includes(definition.id)) { operations.push(definition.id); } else { @@ -31,7 +30,7 @@ describe('explainOperator', () => { } test('Resolves operation definitions', () => { - expect(definitions.length).toBeGreaterThan(0); + expect(operationDefinitions.length).toBeGreaterThan(0); }); test.each(operations)('Returns docs for the %s operation', (operation) => { diff --git a/public/app/plugins/datasource/loki/querybuilder/operations.ts b/public/app/plugins/datasource/loki/querybuilder/operations.ts index ddd94ac2102..1ecdec52d9e 100644 --- a/public/app/plugins/datasource/loki/querybuilder/operations.ts +++ b/public/app/plugins/datasource/loki/querybuilder/operations.ts @@ -1,4 +1,4 @@ -import { QueryBuilderOperationDef, QueryBuilderOperationParamValue } from '../../prometheus/querybuilder/shared/types'; +import { QueryBuilderOperationDefinition, QueryBuilderOperationParamValue } from '@grafana/experimental'; import { binaryScalarOperations } from './binaryScalarOperations'; import { UnwrapParamEditor } from './components/UnwrapParamEditor'; @@ -15,7 +15,7 @@ import { } from './operationUtils'; import { LokiOperationId, LokiOperationOrder, lokiOperators, LokiVisualQueryOperationCategory } from './types'; -export function getOperationDefinitions(): QueryBuilderOperationDef[] { +function getOperationDefinitions(): QueryBuilderOperationDefinition[] { const aggregations = [ LokiOperationId.Sum, LokiOperationId.Min, @@ -66,7 +66,7 @@ export function getOperationDefinitions(): QueryBuilderOperationDef[] { ...createRangeOperationWithGrouping(LokiOperationId.QuantileOverTime), ]; - const list: QueryBuilderOperationDef[] = [ + const list: QueryBuilderOperationDefinition[] = [ ...aggregations, ...aggregationsWithParam, ...rangeOperations, @@ -581,14 +581,14 @@ Example: \`\`error_level=\`level\` \`\` } // Keeping a local copy as an optimization measure. -const definitions = getOperationDefinitions(); +export const operationDefinitions = getOperationDefinitions(); /** * Given an operator, return the corresponding explain. * For usage within the Query Editor. */ export function explainOperator(id: LokiOperationId | string): string { - const definition = definitions.find((operation) => operation.id === id); + const definition = operationDefinitions.find((operation) => operation.id === id); const explain = definition?.explainHandler?.({ id: '', params: [''] }) || ''; @@ -596,11 +596,14 @@ export function explainOperator(id: LokiOperationId | string): string { return explain.replace(/\[(.*)\]\(.*\)/g, '$1'); } -export function getDefinitionById(id: string): QueryBuilderOperationDef | undefined { - return definitions.find((x) => x.id === id); +export function getDefinitionById(id: string): QueryBuilderOperationDefinition | undefined { + return operationDefinitions.find((x) => x.id === id); } -export function checkParamsAreValid(def: QueryBuilderOperationDef, params: QueryBuilderOperationParamValue[]): boolean { +export function checkParamsAreValid( + def: QueryBuilderOperationDefinition, + params: QueryBuilderOperationParamValue[] +): boolean { // For now we only check if the operation has all the required params. if (params.length < def.params.filter((param) => !param.optional).length) { return false; diff --git a/public/app/plugins/datasource/loki/querybuilder/parsing.ts b/public/app/plugins/datasource/loki/querybuilder/parsing.ts index 45007fff9e8..ea2f0003e84 100644 --- a/public/app/plugins/datasource/loki/querybuilder/parsing.ts +++ b/public/app/plugins/datasource/loki/querybuilder/parsing.ts @@ -1,5 +1,6 @@ import { SyntaxNode } from '@lezer/common'; +import { QueryBuilderLabelFilter, QueryBuilderOperation, QueryBuilderOperationParamValue } from '@grafana/experimental'; import { And, BinOpExpr, @@ -54,12 +55,6 @@ import { OrFilter, } from '@grafana/lezer-logql'; -import { - QueryBuilderLabelFilter, - QueryBuilderOperation, - QueryBuilderOperationParamValue, -} from '../../prometheus/querybuilder/shared/types'; - import { binaryScalarDefs } from './binaryScalarOperations'; import { checkParamsAreValid, getDefinitionById } from './operations'; import { diff --git a/public/app/plugins/datasource/loki/querybuilder/parsingUtils.ts b/public/app/plugins/datasource/loki/querybuilder/parsingUtils.ts index ba642ae3c96..adc17113435 100644 --- a/public/app/plugins/datasource/loki/querybuilder/parsingUtils.ts +++ b/public/app/plugins/datasource/loki/querybuilder/parsingUtils.ts @@ -1,6 +1,6 @@ import { SyntaxNode, TreeCursor } from '@lezer/common'; -import { QueryBuilderOperation, QueryBuilderOperationParamValue } from '../../prometheus/querybuilder/shared/types'; +import { QueryBuilderOperation, QueryBuilderOperationParamValue } from '@grafana/experimental'; // Although 0 isn't explicitly provided in the @grafana/lezer-logql library as the error node ID, it does appear to be the ID of error nodes within lezer. export const ErrorId = 0; diff --git a/public/app/plugins/datasource/loki/querybuilder/state.test.ts b/public/app/plugins/datasource/loki/querybuilder/state.test.ts index 2c59c4d9598..8335414f6e6 100644 --- a/public/app/plugins/datasource/loki/querybuilder/state.test.ts +++ b/public/app/plugins/datasource/loki/querybuilder/state.test.ts @@ -1,4 +1,4 @@ -import { QueryEditorMode } from '../../prometheus/querybuilder/shared/types'; +import { QueryEditorMode } from '@grafana/experimental'; import { changeEditorMode, getQueryWithDefaults } from './state'; diff --git a/public/app/plugins/datasource/loki/querybuilder/state.ts b/public/app/plugins/datasource/loki/querybuilder/state.ts index 8423080a7bc..590aeffdd8a 100644 --- a/public/app/plugins/datasource/loki/querybuilder/state.ts +++ b/public/app/plugins/datasource/loki/querybuilder/state.ts @@ -1,4 +1,5 @@ -import { QueryEditorMode } from '../../prometheus/querybuilder/shared/types'; +import { QueryEditorMode } from '@grafana/experimental'; + import { LokiQuery, LokiQueryType } from '../types'; const queryEditorModeDefaultLocalStorageKey = 'LokiQueryEditorModeDefault'; diff --git a/public/app/plugins/datasource/loki/querybuilder/types.ts b/public/app/plugins/datasource/loki/querybuilder/types.ts index fc08ca7a487..c6631fcf164 100644 --- a/public/app/plugins/datasource/loki/querybuilder/types.ts +++ b/public/app/plugins/datasource/loki/querybuilder/types.ts @@ -1,5 +1,9 @@ -import { VisualQueryBinary } from '../../prometheus/querybuilder/shared/LokiAndPromQueryModellerBase'; -import { QueryBuilderLabelFilter, QueryBuilderOperation } from '../../prometheus/querybuilder/shared/types'; +import { + VisualQueryBinary, + QueryBuilderLabelFilter, + QueryBuilderOperation, + BINARY_OPERATIONS_KEY, +} from '@grafana/experimental'; /** * Visual query model @@ -29,7 +33,7 @@ export enum LokiVisualQueryOperationCategory { Formats = 'Formats', LineFilters = 'Line filters', LabelFilters = 'Label filters', - BinaryOps = 'Binary operations', + BinaryOps = BINARY_OPERATIONS_KEY, } export enum LokiOperationId { diff --git a/public/app/plugins/datasource/loki/tracking.test.ts b/public/app/plugins/datasource/loki/tracking.test.ts index 9e22d1dd1b5..6dbd998592b 100644 --- a/public/app/plugins/datasource/loki/tracking.test.ts +++ b/public/app/plugins/datasource/loki/tracking.test.ts @@ -1,8 +1,7 @@ import { DashboardLoadedEvent, DataQueryRequest, dateTime } from '@grafana/data'; +import { QueryEditorMode } from '@grafana/experimental'; import { reportInteraction } from '@grafana/runtime'; -import { QueryEditorMode } from '../prometheus/querybuilder/shared/types'; - import pluginJson from './plugin.json'; import { partitionTimeRange } from './querySplitting'; import { onDashboardLoadedHandler, trackGroupedQueries, trackQuery } from './tracking'; diff --git a/public/app/plugins/datasource/loki/tracking.ts b/public/app/plugins/datasource/loki/tracking.ts index 976a8b0c888..b48f636bc40 100644 --- a/public/app/plugins/datasource/loki/tracking.ts +++ b/public/app/plugins/datasource/loki/tracking.ts @@ -1,8 +1,7 @@ import { CoreApp, DashboardLoadedEvent, DataQueryRequest, DataQueryResponse } from '@grafana/data'; +import { QueryEditorMode } from '@grafana/experimental'; import { reportInteraction, config } from '@grafana/runtime'; -import { QueryEditorMode } from '../prometheus/querybuilder/shared/types'; - import { REF_ID_STARTER_ANNOTATION, REF_ID_DATA_SAMPLES,