Loki Monaco Editor: grab operator documentation from the operations module (#57525)

* feat(loki-operations): add method to resolve operation docs

* feat(loki-operations): read operation and strip markdown links

* feat(loki-monaco-editor): read parser docs from operations

* feat(loki-monaco-editor): grab docs for more operations

* Chore: update completions test

* Chore: add tests for the operations module

* Chore: fix typo

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>

* Chore: fix typo

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
pull/57684/head
Matias Chomicki 3 years ago committed by GitHub
parent e81ce1dae7
commit c8689fd591
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 25
      public/app/plugins/datasource/loki/components/monaco-query-field/monaco-completion-provider/completions.test.ts
  2. 24
      public/app/plugins/datasource/loki/components/monaco-query-field/monaco-completion-provider/completions.ts
  3. 50
      public/app/plugins/datasource/loki/querybuilder/operations.test.ts
  4. 16
      public/app/plugins/datasource/loki/querybuilder/operations.ts
  5. 2
      public/app/plugins/datasource/prometheus/querybuilder/shared/types.ts

@ -6,6 +6,10 @@ import { CompletionDataProvider } from './CompletionDataProvider';
import { getCompletions } from './completions';
import { Label, Situation } from './situation';
jest.mock('../../../querybuilder/operations', () => ({
explainOperator: () => 'Operator docs',
}));
const history = [
{
ts: 12345678,
@ -35,59 +39,59 @@ const otherLabels: Label[] = [
];
const afterSelectorCompletions = [
{
documentation: 'Log line contains string',
documentation: 'Operator docs',
insertText: '|= "$0"',
isSnippet: true,
label: '|= ""',
type: 'LINE_FILTER',
},
{
documentation: 'Log line does not contain string',
documentation: 'Operator docs',
insertText: '!= "$0"',
isSnippet: true,
label: '!= ""',
type: 'LINE_FILTER',
},
{
documentation: 'Log line contains a match to the regular expression',
documentation: 'Operator docs',
insertText: '|~ "$0"',
isSnippet: true,
label: '|~ ""',
type: 'LINE_FILTER',
},
{
documentation: 'Log line does not contain a match to the regular expression',
documentation: 'Operator docs',
insertText: '!~ "$0"',
isSnippet: true,
label: '!~ ""',
type: 'LINE_FILTER',
},
{
documentation: 'Parse and extract labels from the log content.',
documentation: 'Operator docs',
insertText: '',
label: '// Placeholder for the detected parser',
type: 'DETECTED_PARSER_PLACEHOLDER',
},
{
documentation: 'Parse and extract labels from the log content.',
documentation: 'Operator docs',
insertText: '',
label: '// Placeholder for logfmt or json',
type: 'OPPOSITE_PARSER_PLACEHOLDER',
},
{
documentation: 'Parse and extract labels from the log content.',
documentation: 'Operator docs',
insertText: '| pattern',
label: 'pattern',
type: 'PARSER',
},
{
documentation: 'Parse and extract labels from the log content.',
documentation: 'Operator docs',
insertText: '| regexp',
label: 'regexp',
type: 'PARSER',
},
{
documentation: 'Parse and extract labels from the log content.',
documentation: 'Operator docs',
insertText: '| unpack',
label: 'unpack',
type: 'PARSER',
@ -106,18 +110,21 @@ const afterSelectorCompletions = [
insertText: '| unwrap',
label: 'unwrap',
type: 'PIPE_OPERATION',
documentation: 'Operator docs',
},
{
insertText: '| line_format "{{.$0}}"',
isSnippet: true,
label: 'line_format',
type: 'PIPE_OPERATION',
documentation: 'Operator docs',
},
{
insertText: '| label_format',
isSnippet: true,
label: 'label_format',
type: 'PIPE_OPERATION',
documentation: 'Operator docs',
},
];

@ -1,4 +1,6 @@
import { escapeLabelValueInExactSelector } from '../../../languageUtils';
import { explainOperator } from '../../../querybuilder/operations';
import { LokiOperationId } from '../../../querybuilder/types';
import { AGGREGATION_OPERATORS, RANGE_VEC_FUNCTIONS } from '../../../syntax';
import { CompletionDataProvider } from './CompletionDataProvider';
@ -67,21 +69,21 @@ const DURATION_COMPLETIONS: Completion[] = ['$__interval', '$__range', '1m', '5m
const LINE_FILTER_COMPLETIONS = [
{
operator: '|=',
documentation: 'Log line contains string',
documentation: explainOperator(LokiOperationId.LineContains),
afterPipe: true,
},
{
operator: '!=',
documentation: 'Log line does not contain string',
documentation: explainOperator(LokiOperationId.LineContainsNot),
},
{
operator: '|~',
documentation: 'Log line contains a match to the regular expression',
documentation: explainOperator(LokiOperationId.LineMatchesRegex),
afterPipe: true,
},
{
operator: '!~',
documentation: 'Log line does not contain a match to the regular expression',
documentation: explainOperator(LokiOperationId.LineMatchesRegexNot),
},
];
@ -152,7 +154,6 @@ async function getInGroupingCompletions(
}
const PARSERS = ['json', 'logfmt', 'pattern', 'regexp', 'unpack'];
const PARSER_DOCUMENTATION = 'Parse and extract labels from the log content.';
async function getAfterSelectorCompletions(
labels: Label[],
@ -171,7 +172,9 @@ async function getAfterSelectorCompletions(
type: 'PARSER',
label: `json${extra}`,
insertText: `${prefix}json`,
documentation: hasLevelInExtractedLabels ? 'Use it to get log-levels in the histogram' : PARSER_DOCUMENTATION,
documentation: hasLevelInExtractedLabels
? 'Use it to get log-levels in the histogram'
: explainOperator(LokiOperationId.Json),
});
}
@ -182,7 +185,9 @@ async function getAfterSelectorCompletions(
type: 'DURATION',
label: `logfmt${extra}`,
insertText: `${prefix}logfmt`,
documentation: hasLevelInExtractedLabels ? 'Get detected levels in the histogram' : PARSER_DOCUMENTATION,
documentation: hasLevelInExtractedLabels
? 'Get detected levels in the histogram'
: explainOperator(LokiOperationId.Logfmt),
});
}
@ -192,7 +197,7 @@ async function getAfterSelectorCompletions(
type: 'PARSER',
label: parser,
insertText: `${prefix}${parser}`,
documentation: PARSER_DOCUMENTATION,
documentation: explainOperator(parser),
});
});
@ -208,6 +213,7 @@ async function getAfterSelectorCompletions(
type: 'PIPE_OPERATION',
label: 'unwrap',
insertText: `${prefix}unwrap`,
documentation: explainOperator(LokiOperationId.Unwrap),
});
completions.push({
@ -215,6 +221,7 @@ async function getAfterSelectorCompletions(
label: 'line_format',
insertText: `${prefix}line_format "{{.$0}}"`,
isSnippet: true,
documentation: explainOperator(LokiOperationId.LineFormat),
});
completions.push({
@ -222,6 +229,7 @@ async function getAfterSelectorCompletions(
label: 'label_format',
insertText: `${prefix}label_format`,
isSnippet: true,
documentation: explainOperator(LokiOperationId.LabelFormat),
});
return [...getLineFilterCompletions(afterPipe), ...completions];

@ -0,0 +1,50 @@
import { explainOperator, getOperationDefinitions } from './operations';
import { LokiOperationId } from './types';
const undocumentedOperationsIds: string[] = [
LokiOperationId.Addition,
LokiOperationId.Subtraction,
LokiOperationId.MultiplyBy,
LokiOperationId.DivideBy,
LokiOperationId.Modulo,
LokiOperationId.Exponent,
LokiOperationId.NestedQuery,
LokiOperationId.EqualTo,
LokiOperationId.NotEqualTo,
LokiOperationId.GreaterThan,
LokiOperationId.LessThan,
LokiOperationId.GreaterOrEqual,
LokiOperationId.LessOrEqual,
];
describe('explainOperator', () => {
let operations = [];
let undocumentedOperations = [];
const definitions = getOperationDefinitions();
for (const definition of definitions) {
if (!undocumentedOperationsIds.includes(definition.id)) {
operations.push(definition.id);
} else {
undocumentedOperations.push(definition.id);
}
}
test('Resolves operation definitions', () => {
expect(definitions.length).toBeGreaterThan(0);
});
test.each(operations)('Returns docs for the %s operation', (operation) => {
const explain = explainOperator(operation);
expect(explain).toBeDefined();
expect(explain).not.toBe('');
});
test.each(undocumentedOperations)('Returns empty docs for the %s operation', (operation) => {
const explain = explainOperator(operation);
expect(explain).toBeDefined();
expect(explain).toBe('');
});
});

@ -420,3 +420,19 @@ export function getOperationDefinitions(): QueryBuilderOperationDef[] {
return list;
}
// Keeping a local copy as an optimization measure.
const definitions = 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 explain = definition?.explainHandler?.({ id: '', params: ['<value>'] }) || '';
// Strip markdown links
return explain.replace(/\[(.*)\]\(.*\)/g, '$1');
}

@ -43,7 +43,7 @@ export type QueryBuilderAddOperationHandler<T> = (
modeller: VisualQueryModeller
) => T;
export type QueryBuilderExplainOperationHandler = (op: QueryBuilderOperation, def: QueryBuilderOperationDef) => string;
export type QueryBuilderExplainOperationHandler = (op: QueryBuilderOperation, def?: QueryBuilderOperationDef) => string;
export type QueryBuilderOnParamChangedHandler = (
index: number,

Loading…
Cancel
Save