2018-11-13 09:35:20 -06:00
|
|
|
import Plain from 'slate-plain-serializer';
|
|
|
|
|
2022-10-06 09:35:30 -05:00
|
|
|
import { AbstractLabelOperator, DataFrame } from '@grafana/data';
|
2019-10-31 03:27:01 -05:00
|
|
|
import { TypeaheadInput } from '@grafana/ui';
|
2019-09-23 06:26:05 -05:00
|
|
|
|
2022-09-08 07:32:32 -05:00
|
|
|
import LanguageProvider, { LokiHistoryItem } from './LanguageProvider';
|
2022-03-29 10:49:21 -05:00
|
|
|
import { LokiDatasource } from './datasource';
|
2022-08-26 02:26:48 -05:00
|
|
|
import { createLokiDatasource, createMetadataRequest } from './mocks';
|
2023-01-16 06:07:02 -06:00
|
|
|
import {
|
|
|
|
extractLogParserFromDataFrame,
|
|
|
|
extractLabelKeysFromDataFrame,
|
|
|
|
extractUnwrapLabelKeysFromDataFrame,
|
|
|
|
} from './responseUtils';
|
2022-01-07 12:00:11 -06:00
|
|
|
import { LokiQueryType } from './types';
|
2018-11-13 09:35:20 -06:00
|
|
|
|
2022-10-06 09:35:30 -05:00
|
|
|
jest.mock('./responseUtils');
|
|
|
|
|
2019-11-15 09:38:25 -06:00
|
|
|
jest.mock('app/store/store', () => ({
|
|
|
|
store: {
|
|
|
|
getState: jest.fn().mockReturnValue({
|
|
|
|
explore: {
|
|
|
|
left: {
|
|
|
|
mode: 'Logs',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
}));
|
|
|
|
|
2018-11-13 09:35:20 -06:00
|
|
|
describe('Language completion provider', () => {
|
2022-08-26 02:26:48 -05:00
|
|
|
const datasource = setup({});
|
2018-11-13 09:35:20 -06:00
|
|
|
|
2019-12-31 02:53:30 -06:00
|
|
|
describe('query suggestions', () => {
|
|
|
|
it('returns no suggestions on empty context', async () => {
|
2018-11-22 04:02:53 -06:00
|
|
|
const instance = new LanguageProvider(datasource);
|
2018-11-30 08:13:53 -06:00
|
|
|
const value = Plain.deserialize('');
|
2019-09-23 06:26:05 -05:00
|
|
|
const result = await instance.provideCompletionItems({ text: '', prefix: '', value, wrapperClasses: [] });
|
2018-11-22 04:02:53 -06:00
|
|
|
expect(result.context).toBeUndefined();
|
2019-09-23 06:26:05 -05:00
|
|
|
|
2019-12-31 02:53:30 -06:00
|
|
|
expect(result.suggestions.length).toEqual(0);
|
2018-11-22 04:02:53 -06:00
|
|
|
});
|
|
|
|
|
2019-12-31 02:53:30 -06:00
|
|
|
it('returns history on empty context when history was provided', async () => {
|
2018-11-22 04:02:53 -06:00
|
|
|
const instance = new LanguageProvider(datasource);
|
|
|
|
const value = Plain.deserialize('');
|
2019-09-12 03:02:49 -05:00
|
|
|
const history: LokiHistoryItem[] = [
|
2018-11-22 04:02:53 -06:00
|
|
|
{
|
|
|
|
query: { refId: '1', expr: '{app="foo"}' },
|
2019-09-12 03:02:49 -05:00
|
|
|
ts: 1,
|
2018-11-22 04:02:53 -06:00
|
|
|
},
|
|
|
|
];
|
2019-09-23 06:26:05 -05:00
|
|
|
const result = await instance.provideCompletionItems(
|
2019-07-08 10:14:48 -05:00
|
|
|
{ text: '', prefix: '', value, wrapperClasses: [] },
|
2021-03-02 09:58:14 -06:00
|
|
|
{ history }
|
2019-07-08 10:14:48 -05:00
|
|
|
);
|
2018-11-22 04:02:53 -06:00
|
|
|
expect(result.context).toBeUndefined();
|
2019-09-23 06:26:05 -05:00
|
|
|
|
2018-11-22 04:02:53 -06:00
|
|
|
expect(result.suggestions).toMatchObject([
|
|
|
|
{
|
|
|
|
label: 'History',
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
label: '{app="foo"}',
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
});
|
2018-11-30 08:13:53 -06:00
|
|
|
|
2019-12-31 02:53:30 -06:00
|
|
|
it('returns function and history suggestions', async () => {
|
2018-11-30 08:13:53 -06:00
|
|
|
const instance = new LanguageProvider(datasource);
|
2019-12-31 02:53:30 -06:00
|
|
|
const input = createTypeaheadInput('m', 'm', undefined, 1, [], instance);
|
|
|
|
// Historic expressions don't have to match input, filtering is done in field
|
2019-09-12 03:02:49 -05:00
|
|
|
const history: LokiHistoryItem[] = [
|
2018-11-30 08:13:53 -06:00
|
|
|
{
|
|
|
|
query: { refId: '1', expr: '{app="foo"}' },
|
2019-09-12 03:02:49 -05:00
|
|
|
ts: 1,
|
2018-11-30 08:13:53 -06:00
|
|
|
},
|
|
|
|
];
|
2019-09-23 06:26:05 -05:00
|
|
|
const result = await instance.provideCompletionItems(input, { history });
|
2018-11-30 08:13:53 -06:00
|
|
|
expect(result.context).toBeUndefined();
|
2019-12-31 02:53:30 -06:00
|
|
|
expect(result.suggestions.length).toEqual(2);
|
|
|
|
expect(result.suggestions[0].label).toEqual('History');
|
|
|
|
expect(result.suggestions[1].label).toEqual('Functions');
|
2018-11-30 08:13:53 -06:00
|
|
|
});
|
2020-10-14 03:22:39 -05:00
|
|
|
|
|
|
|
it('returns pipe operations on pipe context', async () => {
|
|
|
|
const instance = new LanguageProvider(datasource);
|
|
|
|
const input = createTypeaheadInput('{app="test"} | ', ' ', '', 15, ['context-pipe']);
|
2021-03-02 09:58:14 -06:00
|
|
|
const result = await instance.provideCompletionItems(input);
|
2020-10-14 03:22:39 -05:00
|
|
|
expect(result.context).toBeUndefined();
|
|
|
|
expect(result.suggestions.length).toEqual(2);
|
|
|
|
expect(result.suggestions[0].label).toEqual('Operators');
|
|
|
|
expect(result.suggestions[1].label).toEqual('Parsers');
|
|
|
|
});
|
2018-11-13 09:35:20 -06:00
|
|
|
});
|
|
|
|
|
2021-09-07 08:44:45 -05:00
|
|
|
describe('fetchSeries', () => {
|
|
|
|
it('should use match[] parameter', () => {
|
2022-08-26 02:26:48 -05:00
|
|
|
const datasource = setup({}, { '{foo="bar"}': [{ label1: 'label_val1' }] });
|
2021-09-07 08:44:45 -05:00
|
|
|
const languageProvider = new LanguageProvider(datasource);
|
|
|
|
const fetchSeries = languageProvider.fetchSeries;
|
|
|
|
const requestSpy = jest.spyOn(languageProvider, 'request');
|
|
|
|
fetchSeries('{job="grafana"}');
|
2022-04-28 07:28:57 -05:00
|
|
|
expect(requestSpy).toHaveBeenCalledWith('series', {
|
2021-09-07 08:44:45 -05:00
|
|
|
end: 1560163909000,
|
|
|
|
'match[]': '{job="grafana"}',
|
|
|
|
start: 1560153109000,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2022-02-03 04:40:19 -06:00
|
|
|
describe('fetchSeriesLabels', () => {
|
|
|
|
it('should interpolate variable in series', () => {
|
2022-08-26 02:26:48 -05:00
|
|
|
const datasource = setup({});
|
|
|
|
jest.spyOn(datasource, 'getTimeRangeParams').mockReturnValue({ start: 0, end: 1 });
|
|
|
|
jest
|
|
|
|
.spyOn(datasource, 'interpolateString')
|
|
|
|
.mockImplementation((string: string) => string.replace(/\$/, 'interpolated-'));
|
2022-02-03 04:40:19 -06:00
|
|
|
|
|
|
|
const languageProvider = new LanguageProvider(datasource);
|
|
|
|
const fetchSeriesLabels = languageProvider.fetchSeriesLabels;
|
|
|
|
const requestSpy = jest.spyOn(languageProvider, 'request').mockResolvedValue([]);
|
|
|
|
fetchSeriesLabels('$stream');
|
|
|
|
expect(requestSpy).toHaveBeenCalled();
|
2022-04-28 07:28:57 -05:00
|
|
|
expect(requestSpy).toHaveBeenCalledWith('series', {
|
2022-02-03 04:40:19 -06:00
|
|
|
end: 1,
|
|
|
|
'match[]': 'interpolated-stream',
|
|
|
|
start: 0,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-03-04 10:17:02 -06:00
|
|
|
describe('label key suggestions', () => {
|
|
|
|
it('returns all label suggestions on empty selector', async () => {
|
2022-08-26 02:26:48 -05:00
|
|
|
const datasource = setup({ label1: [], label2: [] });
|
2020-03-04 10:17:02 -06:00
|
|
|
const provider = await getLanguageProvider(datasource);
|
|
|
|
const input = createTypeaheadInput('{}', '', '', 1);
|
2021-03-02 09:58:14 -06:00
|
|
|
const result = await provider.provideCompletionItems(input);
|
2018-11-13 09:35:20 -06:00
|
|
|
expect(result.context).toBe('context-labels');
|
2020-09-21 10:56:20 -05:00
|
|
|
expect(result.suggestions).toEqual([
|
|
|
|
{
|
|
|
|
items: [
|
|
|
|
{ label: 'label1', filterText: '"label1"' },
|
|
|
|
{ label: 'label2', filterText: '"label2"' },
|
|
|
|
],
|
|
|
|
label: 'Labels',
|
|
|
|
},
|
|
|
|
]);
|
2018-11-13 09:35:20 -06:00
|
|
|
});
|
2019-09-12 03:02:49 -05:00
|
|
|
|
2020-03-04 10:17:02 -06:00
|
|
|
it('returns all label suggestions on selector when starting to type', async () => {
|
2022-08-26 02:26:48 -05:00
|
|
|
const datasource = setup({ label1: [], label2: [] });
|
2019-09-12 03:02:49 -05:00
|
|
|
const provider = await getLanguageProvider(datasource);
|
2020-03-04 10:17:02 -06:00
|
|
|
const input = createTypeaheadInput('{l}', '', '', 2);
|
2021-03-02 09:58:14 -06:00
|
|
|
const result = await provider.provideCompletionItems(input);
|
2019-09-12 03:02:49 -05:00
|
|
|
expect(result.context).toBe('context-labels');
|
2020-09-21 10:56:20 -05:00
|
|
|
expect(result.suggestions).toEqual([
|
|
|
|
{
|
|
|
|
items: [
|
|
|
|
{ label: 'label1', filterText: '"label1"' },
|
|
|
|
{ label: 'label2', filterText: '"label2"' },
|
|
|
|
],
|
|
|
|
label: 'Labels',
|
|
|
|
},
|
|
|
|
]);
|
2019-09-12 03:02:49 -05:00
|
|
|
});
|
2020-03-04 10:17:02 -06:00
|
|
|
});
|
2019-09-12 03:02:49 -05:00
|
|
|
|
2020-03-04 10:17:02 -06:00
|
|
|
describe('label suggestions facetted', () => {
|
|
|
|
it('returns facetted label suggestions based on selector', async () => {
|
2022-08-26 02:26:48 -05:00
|
|
|
const datasource = setup({ label1: [], label2: [] }, { '{foo="bar"}': [{ label1: 'label_val1' }] });
|
2020-03-04 10:17:02 -06:00
|
|
|
const provider = await getLanguageProvider(datasource);
|
|
|
|
const input = createTypeaheadInput('{foo="bar",}', '', '', 11);
|
2021-03-02 09:58:14 -06:00
|
|
|
const result = await provider.provideCompletionItems(input);
|
2020-03-04 10:17:02 -06:00
|
|
|
expect(result.context).toBe('context-labels');
|
|
|
|
expect(result.suggestions).toEqual([{ items: [{ label: 'label1' }], label: 'Labels' }]);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('returns facetted label suggestions for multipule selectors', async () => {
|
2022-08-26 02:26:48 -05:00
|
|
|
const datasource = setup({ label1: [], label2: [] }, { '{baz="42",foo="bar"}': [{ label2: 'label_val2' }] });
|
2020-03-04 10:17:02 -06:00
|
|
|
const provider = await getLanguageProvider(datasource);
|
|
|
|
const input = createTypeaheadInput('{baz="42",foo="bar",}', '', '', 20);
|
2021-03-02 09:58:14 -06:00
|
|
|
const result = await provider.provideCompletionItems(input);
|
2020-03-04 10:17:02 -06:00
|
|
|
expect(result.context).toBe('context-labels');
|
|
|
|
expect(result.suggestions).toEqual([{ items: [{ label: 'label2' }], label: 'Labels' }]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('label suggestions', () => {
|
2019-09-12 03:02:49 -05:00
|
|
|
it('returns label values suggestions from Loki', async () => {
|
2022-08-26 02:26:48 -05:00
|
|
|
const datasource = setup({ label1: ['label1_val1', 'label1_val2'], label2: [] });
|
2019-09-12 03:02:49 -05:00
|
|
|
const provider = await getLanguageProvider(datasource);
|
|
|
|
const input = createTypeaheadInput('{label1=}', '=', 'label1');
|
2021-03-02 09:58:14 -06:00
|
|
|
let result = await provider.provideCompletionItems(input);
|
2019-09-23 06:26:05 -05:00
|
|
|
|
2021-03-02 09:58:14 -06:00
|
|
|
result = await provider.provideCompletionItems(input);
|
2019-09-12 03:02:49 -05:00
|
|
|
expect(result.context).toBe('context-label-values');
|
|
|
|
expect(result.suggestions).toEqual([
|
2020-09-21 10:56:20 -05:00
|
|
|
{
|
|
|
|
items: [
|
|
|
|
{ label: 'label1_val1', filterText: '"label1_val1"' },
|
|
|
|
{ label: 'label1_val2', filterText: '"label1_val2"' },
|
|
|
|
],
|
|
|
|
label: 'Label values for "label1"',
|
|
|
|
},
|
2019-09-12 03:02:49 -05:00
|
|
|
]);
|
|
|
|
});
|
2021-03-15 08:11:43 -05:00
|
|
|
it('returns label values suggestions from Loki when re-editing', async () => {
|
2022-08-26 02:26:48 -05:00
|
|
|
const datasource = setup({ label1: ['label1_val1', 'label1_val2'], label2: [] });
|
2021-03-15 08:11:43 -05:00
|
|
|
const provider = await getLanguageProvider(datasource);
|
|
|
|
const input = createTypeaheadInput('{label1="label1_v"}', 'label1_v', 'label1', 17, [
|
|
|
|
'attr-value',
|
|
|
|
'context-labels',
|
|
|
|
]);
|
|
|
|
let result = await provider.provideCompletionItems(input);
|
|
|
|
expect(result.context).toBe('context-label-values');
|
|
|
|
expect(result.suggestions).toEqual([
|
|
|
|
{
|
|
|
|
items: [
|
|
|
|
{ label: 'label1_val1', filterText: '"label1_val1"' },
|
|
|
|
{ label: 'label1_val2', filterText: '"label1_val2"' },
|
|
|
|
],
|
|
|
|
label: 'Label values for "label1"',
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
});
|
2018-11-13 09:35:20 -06:00
|
|
|
});
|
2020-05-05 07:19:52 -05:00
|
|
|
|
|
|
|
describe('label values', () => {
|
|
|
|
it('should fetch label values if not cached', async () => {
|
2022-08-26 02:26:48 -05:00
|
|
|
const datasource = setup({ testkey: ['label1_val1', 'label1_val2'], label2: [] });
|
2020-05-05 07:19:52 -05:00
|
|
|
const provider = await getLanguageProvider(datasource);
|
|
|
|
const requestSpy = jest.spyOn(provider, 'request');
|
2021-03-02 09:58:14 -06:00
|
|
|
const labelValues = await provider.fetchLabelValues('testkey');
|
2020-05-05 07:19:52 -05:00
|
|
|
expect(requestSpy).toHaveBeenCalled();
|
|
|
|
expect(labelValues).toEqual(['label1_val1', 'label1_val2']);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return cached values', async () => {
|
2022-08-26 02:26:48 -05:00
|
|
|
const datasource = setup({ testkey: ['label1_val1', 'label1_val2'], label2: [] });
|
2020-05-05 07:19:52 -05:00
|
|
|
const provider = await getLanguageProvider(datasource);
|
|
|
|
const requestSpy = jest.spyOn(provider, 'request');
|
2021-03-02 09:58:14 -06:00
|
|
|
const labelValues = await provider.fetchLabelValues('testkey');
|
2020-05-05 07:19:52 -05:00
|
|
|
expect(requestSpy).toHaveBeenCalledTimes(1);
|
|
|
|
expect(labelValues).toEqual(['label1_val1', 'label1_val2']);
|
|
|
|
|
2021-03-02 09:58:14 -06:00
|
|
|
const nextLabelValues = await provider.fetchLabelValues('testkey');
|
2020-05-05 07:19:52 -05:00
|
|
|
expect(requestSpy).toHaveBeenCalledTimes(1);
|
|
|
|
expect(nextLabelValues).toEqual(['label1_val1', 'label1_val2']);
|
|
|
|
});
|
2022-06-02 04:30:47 -05:00
|
|
|
|
|
|
|
it('should encode special characters', async () => {
|
2022-08-26 02:26:48 -05:00
|
|
|
const datasource = setup({ '`\\"testkey': ['label1_val1', 'label1_val2'], label2: [] });
|
2022-06-02 04:30:47 -05:00
|
|
|
const provider = await getLanguageProvider(datasource);
|
|
|
|
const requestSpy = jest.spyOn(provider, 'request');
|
|
|
|
await provider.fetchLabelValues('`\\"testkey');
|
|
|
|
|
|
|
|
expect(requestSpy).toHaveBeenCalledWith('label/%60%5C%22testkey/values', expect.any(Object));
|
|
|
|
});
|
2020-05-05 07:19:52 -05:00
|
|
|
});
|
2018-11-13 09:35:20 -06:00
|
|
|
});
|
|
|
|
|
2019-07-08 10:14:48 -05:00
|
|
|
describe('Request URL', () => {
|
|
|
|
it('should contain range params', async () => {
|
2022-08-26 02:26:48 -05:00
|
|
|
const datasourceWithLabels = setup({ other: [] });
|
2021-03-02 09:58:14 -06:00
|
|
|
const rangeParams = datasourceWithLabels.getTimeRangeParams();
|
2022-08-26 02:26:48 -05:00
|
|
|
const datasourceSpy = jest.spyOn(datasourceWithLabels, 'metadataRequest');
|
2019-07-08 10:14:48 -05:00
|
|
|
|
2021-03-02 09:58:14 -06:00
|
|
|
const instance = new LanguageProvider(datasourceWithLabels);
|
2021-05-12 04:49:20 -05:00
|
|
|
instance.fetchLabels();
|
2022-04-28 07:28:57 -05:00
|
|
|
const expectedUrl = 'labels';
|
2021-03-02 09:58:14 -06:00
|
|
|
expect(datasourceSpy).toHaveBeenCalledWith(expectedUrl, rangeParams);
|
2019-07-08 10:14:48 -05:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2023-03-09 04:36:07 -06:00
|
|
|
describe('fetchLabels', () => {
|
|
|
|
it('should return labels', async () => {
|
|
|
|
const datasourceWithLabels = setup({ other: [] });
|
|
|
|
|
|
|
|
const instance = new LanguageProvider(datasourceWithLabels);
|
|
|
|
const labels = await instance.fetchLabels();
|
|
|
|
expect(labels).toEqual(['other']);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should set labels', async () => {
|
|
|
|
const datasourceWithLabels = setup({ other: [] });
|
|
|
|
|
|
|
|
const instance = new LanguageProvider(datasourceWithLabels);
|
|
|
|
await instance.fetchLabels();
|
|
|
|
expect(instance.labelKeys).toEqual(['other']);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return empty array', async () => {
|
|
|
|
const datasourceWithLabels = setup({});
|
|
|
|
|
|
|
|
const instance = new LanguageProvider(datasourceWithLabels);
|
|
|
|
const labels = await instance.fetchLabels();
|
|
|
|
expect(labels).toEqual([]);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should set empty array', async () => {
|
|
|
|
const datasourceWithLabels = setup({});
|
|
|
|
|
|
|
|
const instance = new LanguageProvider(datasourceWithLabels);
|
|
|
|
await instance.fetchLabels();
|
|
|
|
expect(instance.labelKeys).toEqual([]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-11-13 09:35:20 -06:00
|
|
|
describe('Query imports', () => {
|
2022-08-26 02:26:48 -05:00
|
|
|
const datasource = setup({});
|
2018-11-13 09:35:20 -06:00
|
|
|
|
2021-12-14 07:36:47 -06:00
|
|
|
it('returns empty queries', async () => {
|
2021-03-02 09:58:14 -06:00
|
|
|
const instance = new LanguageProvider(datasource);
|
2021-12-14 07:36:47 -06:00
|
|
|
const result = await instance.importFromAbstractQuery({ refId: 'bar', labelMatchers: [] });
|
2022-01-07 12:00:11 -06:00
|
|
|
expect(result).toEqual({ refId: 'bar', expr: '', queryType: LokiQueryType.Range });
|
2018-11-13 09:35:20 -06:00
|
|
|
});
|
|
|
|
|
2021-12-14 07:36:47 -06:00
|
|
|
describe('exporting to abstract query', () => {
|
|
|
|
it('exports labels', async () => {
|
2021-10-12 06:05:57 -05:00
|
|
|
const instance = new LanguageProvider(datasource);
|
2021-12-14 07:36:47 -06:00
|
|
|
const abstractQuery = instance.exportToAbstractQuery({
|
|
|
|
refId: 'bar',
|
|
|
|
expr: '{label1="value1", label2!="value2", label3=~"value3", label4!~"value4"}',
|
|
|
|
instant: true,
|
|
|
|
range: false,
|
|
|
|
});
|
|
|
|
expect(abstractQuery).toMatchObject({
|
|
|
|
refId: 'bar',
|
|
|
|
labelMatchers: [
|
|
|
|
{ name: 'label1', operator: AbstractLabelOperator.Equal, value: 'value1' },
|
|
|
|
{ name: 'label2', operator: AbstractLabelOperator.NotEqual, value: 'value2' },
|
|
|
|
{ name: 'label3', operator: AbstractLabelOperator.EqualRegEx, value: 'value3' },
|
|
|
|
{ name: 'label4', operator: AbstractLabelOperator.NotEqualRegEx, value: 'value4' },
|
|
|
|
],
|
|
|
|
});
|
2018-11-23 04:15:25 -06:00
|
|
|
});
|
2018-11-13 09:35:20 -06:00
|
|
|
});
|
2022-10-06 09:35:30 -05:00
|
|
|
|
|
|
|
describe('getParserAndLabelKeys()', () => {
|
|
|
|
let datasource: LokiDatasource, languageProvider: LanguageProvider;
|
2022-11-07 10:45:07 -06:00
|
|
|
const extractLogParserFromDataFrameMock = jest.mocked(extractLogParserFromDataFrame);
|
|
|
|
const extractedLabelKeys = ['extracted', 'label'];
|
2023-01-16 06:07:02 -06:00
|
|
|
const unwrapLabelKeys = ['unwrap', 'labels'];
|
2022-11-07 10:45:07 -06:00
|
|
|
|
2022-10-06 09:35:30 -05:00
|
|
|
beforeEach(() => {
|
|
|
|
datasource = createLokiDatasource();
|
|
|
|
languageProvider = new LanguageProvider(datasource);
|
2022-11-07 10:45:07 -06:00
|
|
|
jest.mocked(extractLabelKeysFromDataFrame).mockReturnValue(extractedLabelKeys);
|
2023-01-16 06:07:02 -06:00
|
|
|
jest.mocked(extractUnwrapLabelKeysFromDataFrame).mockReturnValue(unwrapLabelKeys);
|
2022-10-06 09:35:30 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
it('identifies selectors with JSON parser data', async () => {
|
|
|
|
jest.spyOn(datasource, 'getDataSamples').mockResolvedValue([{}] as DataFrame[]);
|
2023-03-30 08:18:02 -05:00
|
|
|
extractLogParserFromDataFrameMock.mockReturnValueOnce({ hasLogfmt: false, hasJSON: true, hasPack: false });
|
2022-10-06 09:35:30 -05:00
|
|
|
|
|
|
|
expect(await languageProvider.getParserAndLabelKeys('{place="luna"}')).toEqual({
|
2022-11-07 10:45:07 -06:00
|
|
|
extractedLabelKeys,
|
2023-01-16 06:07:02 -06:00
|
|
|
unwrapLabelKeys,
|
2022-10-06 09:35:30 -05:00
|
|
|
hasJSON: true,
|
|
|
|
hasLogfmt: false,
|
2023-03-30 08:18:02 -05:00
|
|
|
hasPack: false,
|
2022-10-06 09:35:30 -05:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('identifies selectors with Logfmt parser data', async () => {
|
|
|
|
jest.spyOn(datasource, 'getDataSamples').mockResolvedValue([{}] as DataFrame[]);
|
2023-03-30 08:18:02 -05:00
|
|
|
extractLogParserFromDataFrameMock.mockReturnValueOnce({ hasLogfmt: true, hasJSON: false, hasPack: false });
|
2022-10-06 09:35:30 -05:00
|
|
|
|
|
|
|
expect(await languageProvider.getParserAndLabelKeys('{place="luna"}')).toEqual({
|
2022-11-07 10:45:07 -06:00
|
|
|
extractedLabelKeys,
|
2023-01-16 06:07:02 -06:00
|
|
|
unwrapLabelKeys,
|
2022-10-06 09:35:30 -05:00
|
|
|
hasJSON: false,
|
|
|
|
hasLogfmt: true,
|
2023-03-30 08:18:02 -05:00
|
|
|
hasPack: false,
|
2022-10-06 09:35:30 -05:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly processes empty data', async () => {
|
|
|
|
jest.spyOn(datasource, 'getDataSamples').mockResolvedValue([]);
|
|
|
|
extractLogParserFromDataFrameMock.mockClear();
|
|
|
|
|
|
|
|
expect(await languageProvider.getParserAndLabelKeys('{place="luna"}')).toEqual({
|
|
|
|
extractedLabelKeys: [],
|
2023-01-16 06:07:02 -06:00
|
|
|
unwrapLabelKeys: [],
|
2022-10-06 09:35:30 -05:00
|
|
|
hasJSON: false,
|
|
|
|
hasLogfmt: false,
|
2023-03-30 08:18:02 -05:00
|
|
|
hasPack: false,
|
2022-10-06 09:35:30 -05:00
|
|
|
});
|
|
|
|
expect(extractLogParserFromDataFrameMock).not.toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
});
|
2018-11-13 09:35:20 -06:00
|
|
|
});
|
2019-03-25 06:08:28 -05:00
|
|
|
|
2019-09-23 06:26:05 -05:00
|
|
|
async function getLanguageProvider(datasource: LokiDatasource) {
|
2019-09-12 03:02:49 -05:00
|
|
|
const instance = new LanguageProvider(datasource);
|
|
|
|
await instance.start();
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param value Value of the full input
|
|
|
|
* @param text Last piece of text (not sure but in case of {label=} this would be just '=')
|
|
|
|
* @param labelKey Label by which to search for values. Cutting corners a bit here as this should be inferred from value
|
|
|
|
*/
|
|
|
|
function createTypeaheadInput(
|
|
|
|
value: string,
|
|
|
|
text: string,
|
|
|
|
labelKey?: string,
|
|
|
|
anchorOffset?: number,
|
2019-12-31 02:53:30 -06:00
|
|
|
wrapperClasses?: string[],
|
|
|
|
instance?: LanguageProvider
|
2019-09-12 03:02:49 -05:00
|
|
|
): TypeaheadInput {
|
|
|
|
const deserialized = Plain.deserialize(value);
|
2019-09-23 06:26:05 -05:00
|
|
|
const range = deserialized.selection.setAnchor(deserialized.selection.anchor.setOffset(anchorOffset || 1));
|
|
|
|
const valueWithSelection = deserialized.setSelection(range);
|
2019-09-12 03:02:49 -05:00
|
|
|
return {
|
|
|
|
text,
|
2019-12-31 02:53:30 -06:00
|
|
|
prefix: instance ? instance.cleanText(text) : '',
|
2019-09-12 03:02:49 -05:00
|
|
|
wrapperClasses: wrapperClasses || ['context-labels'],
|
|
|
|
value: valueWithSelection,
|
|
|
|
labelKey,
|
|
|
|
};
|
|
|
|
}
|
2022-08-26 02:26:48 -05:00
|
|
|
|
|
|
|
function setup(
|
|
|
|
labelsAndValues: Record<string, string[]>,
|
|
|
|
series?: Record<string, Array<Record<string, string>>>
|
|
|
|
): LokiDatasource {
|
2022-10-06 09:35:30 -05:00
|
|
|
const datasource = createLokiDatasource();
|
2022-08-26 02:26:48 -05:00
|
|
|
|
|
|
|
const rangeMock = {
|
|
|
|
start: 1560153109000,
|
|
|
|
end: 1560163909000,
|
|
|
|
};
|
|
|
|
|
|
|
|
jest.spyOn(datasource, 'getTimeRangeParams').mockReturnValue(rangeMock);
|
|
|
|
jest.spyOn(datasource, 'metadataRequest').mockImplementation(createMetadataRequest(labelsAndValues, series));
|
|
|
|
jest.spyOn(datasource, 'interpolateString').mockImplementation((string: string) => string);
|
|
|
|
|
|
|
|
return datasource;
|
|
|
|
}
|