From bae5b110a4b7228d987b2751213086492ebbf167 Mon Sep 17 00:00:00 2001 From: Brandon Farmer Date: Wed, 6 Jun 2018 15:10:58 -0700 Subject: [PATCH] Allow tags to be filtered with regex --- ui/src/logs/actions/index.ts | 15 +++--- ui/src/logs/components/LogsFilter.tsx | 64 ++++++++---------------- ui/src/logs/components/LogsFilterBar.tsx | 16 +----- ui/src/logs/containers/LogsPage.tsx | 17 ++++--- ui/src/logs/reducers/index.ts | 14 +++--- ui/src/logs/utils/index.ts | 14 ++++-- 6 files changed, 60 insertions(+), 80 deletions(-) diff --git a/ui/src/logs/actions/index.ts b/ui/src/logs/actions/index.ts index 6442a63ac8..09020de7d6 100644 --- a/ui/src/logs/actions/index.ts +++ b/ui/src/logs/actions/index.ts @@ -51,7 +51,7 @@ export enum ActionTypes { SetSearchTerm = 'LOGS_SET_SEARCH_TERM', AddFilter = 'LOGS_ADD_FILTER', RemoveFilter = 'LOGS_REMOVE_FILTER', - SetFilterOperator = 'LOGS_SET_FILTER_OPERATOR', + ChangeFilter = 'LOGS_CHANGE_FILTER', } export interface AddFilterAction { @@ -61,11 +61,12 @@ export interface AddFilterAction { } } -export interface SetFilterOperatorAction { - type: ActionTypes.SetFilterOperator +export interface ChangeFilterAction { + type: ActionTypes.ChangeFilter payload: { id: string operator: string + value: string } } @@ -159,7 +160,7 @@ export type Action = | SetSearchTerm | AddFilterAction | RemoveFilterAction - | SetFilterOperatorAction + | ChangeFilterAction const getTimeRange = (state: State): TimeRange | null => getDeep(state, 'logs.timeRange', null) @@ -182,9 +183,9 @@ const getSearchTerm = (state: State): string | null => const getFilters = (state: State): Filter[] => getDeep(state, 'logs.filters', []) -export const setFilterOperator = (id: string, operator: string) => ({ - type: ActionTypes.SetFilterOperator, - payload: {id, operator}, +export const changeFilter = (id: string, operator: string, value: string) => ({ + type: ActionTypes.ChangeFilter, + payload: {id, operator, value}, }) export const setSource = (source: Source): SetSourceAction => ({ diff --git a/ui/src/logs/components/LogsFilter.tsx b/ui/src/logs/components/LogsFilter.tsx index 3671287a35..c17b8814e0 100644 --- a/ui/src/logs/components/LogsFilter.tsx +++ b/ui/src/logs/components/LogsFilter.tsx @@ -7,12 +7,13 @@ import {ClickOutside} from 'src/shared/components/ClickOutside' interface Props { filter: Filter onDelete: (id: string) => void - onChangeOperator: (id: string, newOperator: string) => void - onChangeValue: (id: string, newValue: string) => void + onChangeFilter: (id: string, newOperator: string, newValue: string) => void } interface State { editing: boolean + value: string + operator: string } class LogsFilter extends PureComponent { @@ -21,14 +22,12 @@ class LogsFilter extends PureComponent { this.state = { editing: false, + value: this.props.filter.value, + operator: this.props.filter.operator, } } public render() { - const { - filter: {id}, - onDelete, - } = this.props const {editing} = this.state return ( @@ -45,7 +44,7 @@ class LogsFilter extends PureComponent { } private handleClickOutside = (): void => { - this.setState({editing: false}) + this.stopEditing() } private handleStartEdit = (): void => { @@ -76,8 +75,9 @@ class LogsFilter extends PureComponent { } private get renderEditor(): JSX.Element { + const {operator, value} = this.state const { - filter: {key, operator, value}, + filter: {key}, } = this.props return ( @@ -106,56 +106,36 @@ class LogsFilter extends PureComponent { } private handleOperatorInput = (e: ChangeEvent): void => { - const { - filter: {id}, - onChangeOperator, - } = this.props + const operator = getDeep(e, 'target.value', '').trim() - const cleanValue = this.enforceOperatorChars(e.target.value) - - onChangeOperator(id, cleanValue) + this.setState({operator}) } private handleValueInput = (e: ChangeEvent): void => { - const { - filter: {id}, - onChangeValue, - } = this.props - - onChangeValue(id, e.target.value) - } - - private enforceOperatorChars = text => { - return text - .split('') - .filter(t => ['!', '~', `=`].includes(t)) - .join('') + const value = getDeep(e, 'target.value', '').trim() + this.setState({value}) } private handleKeyDown = (e: KeyboardEvent): void => { if (e.key === 'Enter') { e.preventDefault() - this.setState({editing: false}) + this.stopEditing() } } - private handleToggleOperator = () => { + private stopEditing(): void { const id = getDeep(this.props, 'filter.id', '') + const {operator, value} = this.state - let nextOperator = '==' - if (this.operator === '==') { - nextOperator = '!=' + let state = {} + if (['!=', '==', '=~'].includes(operator) && value !== '') { + this.props.onChangeFilter(id, operator, value) + } else { + const {filter} = this.props + state = {operator: filter.operator, value: filter.value} } - this.props.onChangeOperator(id, nextOperator) - } - - private get toggleOperatorText(): string { - return this.operator === '==' ? '!=' : '==' - } - - private get operator(): string { - return getDeep(this.props, 'filter.operator', '') + this.setState({...state, editing: false}) } } diff --git a/ui/src/logs/components/LogsFilterBar.tsx b/ui/src/logs/components/LogsFilterBar.tsx index 9557001458..244d4e0d45 100644 --- a/ui/src/logs/components/LogsFilterBar.tsx +++ b/ui/src/logs/components/LogsFilterBar.tsx @@ -6,7 +6,7 @@ interface Props { numResults: number filters: Filter[] onDelete: (id: string) => void - onFilterOperatorChange: (id: string, operator: string) => void + onFilterChange: (id: string, operator: string, value: string) => void } class LogsFilters extends PureComponent { @@ -31,22 +31,10 @@ class LogsFilters extends PureComponent { key={filter.id} filter={filter} onDelete={this.props.onDelete} - onChangeOperator={this.props.onFilterOperatorChange} - onChangeValue={this.handleChangeFilterValue} + onChangeFilter={this.props.onFilterChange} /> )) } - - private handleChangeFilterValue = (id: string, value: string): void => { - // const {filters, onUpdateFilters} = this.props - // const filteredFilters = filters.map(filter => { - // if (filter.id === id) { - // return {...filter, value} - // } - // return filter - // }) - // onUpdateFilters(filteredFilters) - } } export default LogsFilters diff --git a/ui/src/logs/containers/LogsPage.tsx b/ui/src/logs/containers/LogsPage.tsx index 7597dbc390..5d47c4f530 100644 --- a/ui/src/logs/containers/LogsPage.tsx +++ b/ui/src/logs/containers/LogsPage.tsx @@ -10,7 +10,7 @@ import { setSearchTermAsync, addFilter, removeFilter, - setFilterOperator, + changeFilter, } from 'src/logs/actions' import {getSourcesAsync} from 'src/shared/actions/sources' import LogViewerHeader from 'src/logs/components/LogViewerHeader' @@ -38,7 +38,7 @@ interface Props { setSearchTermAsync: (searchTerm: string) => void addFilter: (filter: Filter) => void removeFilter: (id: string) => void - setFilterOperator: (id: string, operator: string) => void + changeFilter: (id: string, operator: string, value: string) => void timeRange: TimeRange histogramData: object[] tableData: { @@ -105,7 +105,7 @@ class LogsPage extends PureComponent { numResults={count} filters={filters || []} onDelete={this.handleFilterDelete} - onFilterOperatorChange={this.handleFilterOperatorChange} + onFilterChange={this.handleFilterChange} /> { id: uuid.v4(), key: selection.key, value: selection.tag, - enabled: true, operator: '==', }) this.props.executeQueriesAsync() @@ -215,8 +214,12 @@ class LogsPage extends PureComponent { this.props.executeQueriesAsync() } - private handleFilterOperatorChange = (id: string, operator: string) => { - this.props.setFilterOperator(id, operator) + private handleFilterChange = ( + id: string, + operator: string, + value: string + ) => { + this.props.changeFilter(id, operator, value) this.props.executeQueriesAsync() } @@ -274,7 +277,7 @@ const mapDispatchToProps = { setSearchTermAsync, addFilter, removeFilter, - setFilterOperator, + changeFilter, } export default connect(mapStateToProps, mapDispatchToProps)(LogsPage) diff --git a/ui/src/logs/reducers/index.ts b/ui/src/logs/reducers/index.ts index 984bf6ae32..6ad7dffb12 100644 --- a/ui/src/logs/reducers/index.ts +++ b/ui/src/logs/reducers/index.ts @@ -4,7 +4,7 @@ import { Action, RemoveFilterAction, AddFilterAction, - SetFilterOperatorAction, + ChangeFilterAction, } from 'src/logs/actions' import {LogsState} from 'src/types/logs' @@ -40,15 +40,15 @@ const addFilter = (state: LogsState, action: AddFilterAction): LogsState => { return {...state, filters: [..._.get(state, 'filters', []), filter]} } -const setFilterOperator = ( +const changeFilter = ( state: LogsState, - action: SetFilterOperatorAction + action: ChangeFilterAction ): LogsState => { - const {id, operator} = action.payload + const {id, operator, value} = action.payload const mappedFilters = _.map(_.get(state, 'filters', []), f => { if (f.id === id) { - return {...f, operator} + return {...f, operator, value} } return f }) @@ -84,8 +84,8 @@ export default (state: LogsState = defaultState, action: Action) => { return addFilter(state, action) case ActionTypes.RemoveFilter: return removeFilter(state, action) - case ActionTypes.SetFilterOperator: - return setFilterOperator(state, action) + case ActionTypes.ChangeFilter: + return changeFilter(state, action) default: return state } diff --git a/ui/src/logs/utils/index.ts b/ui/src/logs/utils/index.ts index 9b0327bfe0..d3fcd23a65 100644 --- a/ui/src/logs/utils/index.ts +++ b/ui/src/logs/utils/index.ts @@ -99,13 +99,21 @@ const operatorMapping = (operator: string): string => { } } +const valueMapping = (operator: string, value): string => { + if (operator === '=~') { + return `${new RegExp(value)}` + } else { + return `'${value}'` + } +} + export const filtersClause = (filters: Filter[]): string => { return _.map( filters, (filter: Filter) => - `"${keyMapping(filter.key)}" ${operatorMapping(filter.operator)} '${ - filter.value - }'` + `"${keyMapping(filter.key)}" ${operatorMapping( + filter.operator + )} ${valueMapping(filter.operator, filter.value)}` ).join(' AND ') }