mirror of https://github.com/grafana/grafana
Logs: Add toggle behavior support for "filter for" and "filter out" label within Logs Details (#70091)
* Datasource test: fix describe nesting * Parsing: export handleQuotes function * Modify query: add functions to detect the presence of a label and remove it * Loki: add support to toggle filters if already present * Datasource test: fix describe nesting * Loki: add support to toggle filter out if present * Remove label: handle escaped values * Datasource: add test case for escaped label values * Loki: remove = filter when applying != * Remove selector: add support for Selector node being far from Matcher * Modify query: add unit tests * Elasticsearch: create modifyQuery for elastic * Elastic modify query: implement functions * Elasticsearch: implement modifyQuery functions in datasource * Elasticsearch: update datasource test * Loki modify query: check for streamSelectorPositions length * Elasticsearch query has filter: escape filter value in regex * Remove unused type * Modify query: use query modeller instance from modulepull/70692/head
parent
4ff0abd0d1
commit
4c4bd69eb6
@ -0,0 +1,74 @@ |
||||
import { queryHasFilter, removeFilterFromQuery, addFilterToQuery } from './modifyQuery'; |
||||
|
||||
describe('queryHasFilter', () => { |
||||
it('should return true if the query contains the positive filter', () => { |
||||
expect(queryHasFilter('label:"value"', 'label', 'value')).toBe(true); |
||||
expect(queryHasFilter('label: "value"', 'label', 'value')).toBe(true); |
||||
expect(queryHasFilter('label : "value"', 'label', 'value')).toBe(true); |
||||
expect(queryHasFilter('label:value', 'label', 'value')).toBe(true); |
||||
expect(queryHasFilter('this:"that" AND label:value', 'label', 'value')).toBe(true); |
||||
expect( |
||||
queryHasFilter( |
||||
'message:"Jun 20 17:19:47 Xtorm syslogd[348]: ASL Sender Statistics"', |
||||
'message', |
||||
'Jun 20 17:19:47 Xtorm syslogd[348]: ASL Sender Statistics' |
||||
) |
||||
).toBe(true); |
||||
}); |
||||
it('should return false if the query does not contain the positive filter', () => { |
||||
expect(queryHasFilter('label:"value"', 'label', 'otherValue')).toBe(false); |
||||
expect(queryHasFilter('-label:"value"', 'label', 'value')).toBe(false); |
||||
}); |
||||
it('should return true if the query contains the negative filter', () => { |
||||
expect(queryHasFilter('-label:"value"', 'label', 'value', '-')).toBe(true); |
||||
expect(queryHasFilter('-label: "value"', 'label', 'value', '-')).toBe(true); |
||||
expect(queryHasFilter('-label : "value"', 'label', 'value', '-')).toBe(true); |
||||
expect(queryHasFilter('-label:value', 'label', 'value', '-')).toBe(true); |
||||
expect(queryHasFilter('this:"that" AND -label:value', 'label', 'value', '-')).toBe(true); |
||||
}); |
||||
it('should return false if the query does not contain the negative filter', () => { |
||||
expect(queryHasFilter('label:"value"', 'label', 'otherValue', '-')).toBe(false); |
||||
expect(queryHasFilter('label:"value"', 'label', 'value', '-')).toBe(false); |
||||
}); |
||||
}); |
||||
|
||||
describe('addFilterToQuery', () => { |
||||
it('should add a positive filter to the query', () => { |
||||
expect(addFilterToQuery('', 'label', 'value')).toBe('label:"value"'); |
||||
}); |
||||
it('should add a positive filter to the query with other filters', () => { |
||||
expect(addFilterToQuery('label2:"value2"', 'label', 'value')).toBe('label2:"value2" AND label:"value"'); |
||||
}); |
||||
it('should add a negative filter to the query', () => { |
||||
expect(addFilterToQuery('', 'label', 'value', '-')).toBe('-label:"value"'); |
||||
}); |
||||
it('should add a negative filter to the query with other filters', () => { |
||||
expect(addFilterToQuery('label2:"value2"', 'label', 'value', '-')).toBe('label2:"value2" AND -label:"value"'); |
||||
}); |
||||
}); |
||||
|
||||
describe('removeFilterFromQuery', () => { |
||||
it('should remove filter from query', () => { |
||||
const query = 'label:"value"'; |
||||
expect(removeFilterFromQuery(query, 'label', 'value')).toBe(''); |
||||
}); |
||||
it('should remove filter from query with other filters', () => { |
||||
expect(removeFilterFromQuery('label:"value" AND label2:"value2"', 'label', 'value')).toBe('label2:"value2"'); |
||||
expect(removeFilterFromQuery('label:value AND label2:"value2"', 'label', 'value')).toBe('label2:"value2"'); |
||||
expect(removeFilterFromQuery('label : "value" OR label2:"value2"', 'label', 'value')).toBe('label2:"value2"'); |
||||
expect(removeFilterFromQuery('test="test" OR label:"value" AND label2:"value2"', 'label', 'value')).toBe( |
||||
'test="test" AND label2:"value2"' |
||||
); |
||||
}); |
||||
it('should not remove the wrong filter', () => { |
||||
expect(removeFilterFromQuery('-label:"value" AND label2:"value2"', 'label', 'value')).toBe( |
||||
'-label:"value" AND label2:"value2"' |
||||
); |
||||
expect(removeFilterFromQuery('label2:"value2" OR -label:value', 'label', 'value')).toBe( |
||||
'label2:"value2" OR -label:value' |
||||
); |
||||
expect(removeFilterFromQuery('-label : "value" OR label2:"value2"', 'label', 'value')).toBe( |
||||
'-label : "value" OR label2:"value2"' |
||||
); |
||||
}); |
||||
}); |
@ -0,0 +1,57 @@ |
||||
import { escapeRegex } from '@grafana/data'; |
||||
|
||||
type ModifierType = '' | '-'; |
||||
|
||||
/** |
||||
* Checks for the presence of a given label:"value" filter in the query. |
||||
*/ |
||||
export function queryHasFilter(query: string, key: string, value: string, modifier: ModifierType = ''): boolean { |
||||
const regex = getFilterRegex(key, value); |
||||
const matches = query.matchAll(regex); |
||||
for (const match of matches) { |
||||
if (modifier === '-' && match[0].startsWith(modifier)) { |
||||
return true; |
||||
} |
||||
if (modifier === '' && !match[0].startsWith('-')) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Adds a label:"value" expression to the query. |
||||
*/ |
||||
export function addFilterToQuery(query: string, key: string, value: string, modifier: ModifierType = ''): string { |
||||
if (queryHasFilter(query, key, value, modifier)) { |
||||
return query; |
||||
} |
||||
|
||||
const filter = `${modifier}${key}:"${value}"`; |
||||
|
||||
return query === '' ? filter : `${query} AND ${filter}`; |
||||
} |
||||
|
||||
function getFilterRegex(key: string, value: string) { |
||||
return new RegExp(`[-]{0,1}\\s*${key}\\s*:\\s*["']{0,1}${escapeRegex(value)}["']{0,1}`, 'ig'); |
||||
} |
||||
|
||||
/** |
||||
* Removes a label:"value" expression from the query. |
||||
*/ |
||||
export function removeFilterFromQuery(query: string, key: string, value: string, modifier: ModifierType = ''): string { |
||||
const regex = getFilterRegex(key, value); |
||||
const matches = query.matchAll(regex); |
||||
const opRegex = new RegExp(`\\s+(?:AND|OR)\\s*$|^\\s*(?:AND|OR)\\s+`, 'ig'); |
||||
for (const match of matches) { |
||||
if (modifier === '-' && match[0].startsWith(modifier)) { |
||||
query = query.replace(regex, '').replace(opRegex, ''); |
||||
} |
||||
if (modifier === '' && !match[0].startsWith('-')) { |
||||
query = query.replace(regex, '').replace(opRegex, ''); |
||||
} |
||||
} |
||||
query = query.replace(/AND\s+OR/gi, 'OR'); |
||||
query = query.replace(/OR\s+AND/gi, 'AND'); |
||||
return query; |
||||
} |
Loading…
Reference in new issue