Cloudwatch: Add syntax highlighting and autocomplete for "Metric Search" (#43985)

* Create a "monarch" folder with everything you need to do syntax highlighting and autocompletion.

* Use this new monarch folder with existing cloudwatch sql.

* Add metric math syntax highlighting and autocomplete.

* Make autocomplete "smarter":
- search always inserts a string as first arg
- strings can't contain predefined functions
- operators follow the last closing )

* Add some tests for Metric Math's CompletionItemProvider.

* Fixes After CR:
- refactor CompletionItemProvider, so that it only requires args that are dynamic or outside of it's responsibility
- Update and add tests with mocked monaco
- Add more autocomplete suggestions for SEARCH expression functions
- sort keywords and give different priority from function to make more visually distinctive.

* Change QueryEditor to auto-resize and look more like the one in Prometheus.

* Add autocomplete for time periods for the third arg of Search.

* More CR fixes:
- fix missing break
- add unit tests for statementPosition
- fix broken time period
- sort time periods

* Bug fix
This commit is contained in:
Sarah Zinger
2022-02-01 22:53:32 -05:00
committed by GitHub
parent b2b584f611
commit 58a71c7e91
46 changed files with 1261 additions and 335 deletions

View File

@@ -0,0 +1,72 @@
import MonacoMock from '../../__mocks__/monarch/Monaco';
import TextModel from '../../__mocks__/monarch/TextModel';
import { MetricMathCompletionItemProvider } from './CompletionItemProvider';
import { getTemplateSrv } from '@grafana/runtime';
import { CloudWatchDatasource } from '../../datasource';
import cloudWatchMetricMathLanguageDefinition from '../definition';
import { Monaco, monacoTypes } from '@grafana/ui';
import { IPosition } from 'monaco-editor';
import {
METRIC_MATH_FNS,
METRIC_MATH_KEYWORDS,
METRIC_MATH_OPERATORS,
METRIC_MATH_PERIODS,
METRIC_MATH_STATISTIC_KEYWORD_STRINGS,
} from '../language';
import * as MetricMathTestData from '../../__mocks__/metric-math-test-data';
const getSuggestions = async (value: string, position: IPosition) => {
const setup = new MetricMathCompletionItemProvider(
({
getVariables: () => [],
getActualRegion: () => 'us-east-2',
} as any) as CloudWatchDatasource,
getTemplateSrv()
);
const monaco = MonacoMock as Monaco;
const provider = setup.getCompletionProvider(monaco, cloudWatchMetricMathLanguageDefinition);
const { suggestions } = await provider.provideCompletionItems(
TextModel(value) as monacoTypes.editor.ITextModel,
position
);
return suggestions;
};
describe('MetricMath: CompletionItemProvider', () => {
describe('getSuggestions', () => {
it('returns a suggestion for every metric math function when the input field is empty', async () => {
const { query, position } = MetricMathTestData.singleLineEmptyQuery;
const suggestions = await getSuggestions(query, position);
expect(suggestions.length).toEqual(METRIC_MATH_FNS.length);
});
it('returns a suggestion for every metric math operator when at the end of a function', async () => {
const { query, position } = MetricMathTestData.afterFunctionQuery;
const suggestions = await getSuggestions(query, position);
expect(suggestions.length).toEqual(METRIC_MATH_OPERATORS.length);
});
it('returns a suggestion for every metric math function and keyword if at the start of the second argument of a function', async () => {
const { query, position } = MetricMathTestData.secondArgQuery;
const suggestions = await getSuggestions(query, position);
expect(suggestions.length).toEqual(METRIC_MATH_FNS.length + METRIC_MATH_KEYWORDS.length);
});
it('does not have any particular suggestions if within a string', async () => {
const { query, position } = MetricMathTestData.withinStringQuery;
const suggestions = await getSuggestions(query, position);
expect(suggestions.length).toEqual(0);
});
it('returns a suggestion for every statistic if the second arg of a search function', async () => {
const { query, position } = MetricMathTestData.secondArgAfterSearchQuery;
const suggestions = await getSuggestions(query, position);
expect(suggestions.length).toEqual(METRIC_MATH_STATISTIC_KEYWORD_STRINGS.length);
});
it('returns a suggestion for every period if the third arg of a search function', async () => {
const { query, position } = MetricMathTestData.thirdArgAfterSearchQuery;
const suggestions = await getSuggestions(query, position);
expect(suggestions.length).toEqual(METRIC_MATH_PERIODS.length);
});
});
});