Skip to content

Commit

Permalink
Merge pull request #4150 from TheThingsNetwork/feature/2231-event-ver…
Browse files Browse the repository at this point in the history
…bosity-filter
  • Loading branch information
johanstokking authored May 12, 2021
2 parents fe9ecab + 9f1aa8c commit 646c4dd
Show file tree
Hide file tree
Showing 26 changed files with 277 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ For details about compatibility between different releases, see the **Commitment
- RPC to find related events by correlation ID.
- CLI command `events find-related`.
- Support for loading Device Repository profiles from different vendors if specified. This allows reusing standard end device profiles from module makers and LoRaWAN end device stack vendors.
- Filtering out verbose events in the event views in the Console.

### Changed

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
"prop-types": "^15.7.2",
"query-string": "^6.14.0",
"react": "^17.0.1",
"react-ace": "^9.3.0",
"react-ace": "^6.6.0",
"react-display-name": "^0.2.5",
"react-dom": "^17.0.1",
"react-grid-system": "^7.1.1",
Expand Down
6 changes: 6 additions & 0 deletions pkg/webui/console/components/events/events.styl
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ $event-container-height = 40px

.actions
background-color: white
display: flex

.verbose-checkbox
margin-top: 0
color: $tc-subtle-gray
margin-right: $cs.s

.widget-container
border-normal()
Expand Down
34 changes: 33 additions & 1 deletion pkg/webui/console/components/events/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ import React, { useState, useCallback } from 'react'
import classnames from 'classnames'

import EVENT_STORE_LIMIT from '@console/constants/event-store-limit'
import { EVENT_VERBOSE_FILTERS_REGEXP } from '@console/constants/event-filters'
import hamburgerMenuClose from '@assets/misc/hamburger-menu-close.svg'

import Button from '@ttn-lw/components/button'
import Checkbox from '@ttn-lw/components/checkbox'
import Icon from '@ttn-lw/components/icon'

import Message from '@ttn-lw/lib/components/message'
Expand All @@ -35,7 +37,18 @@ import { getEventId } from './utils'
import style from './events.styl'

const Events = React.memo(
({ events, scoped, paused, onClear, onPauseToggle, entityId, truncated }) => {
({
events,
scoped,
paused,
onClear,
onPauseToggle,
onFilterChange,
entityId,
truncated,
filter,
disableFiltering,
}) => {
const [focus, setFocus] = useState({ eventId: undefined, visible: false })
const onPause = useCallback(() => onPauseToggle(paused), [onPauseToggle, paused])
const handleRowClick = useCallback(
Expand All @@ -49,6 +62,10 @@ const Events = React.memo(
[focus],
)

const handleVerboseFilterChange = useCallback(() => {
onFilterChange(Boolean(filter) ? undefined : EVENT_VERBOSE_FILTERS_REGEXP)
}, [onFilterChange, filter])

const handleEventInfoCloseClick = useCallback(() => {
setFocus({ eventId: undefined, visible: false })
}, [])
Expand All @@ -65,6 +82,15 @@ const Events = React.memo(
<Message content={m.dataPreview} className={style.cellData} component="div" />
<div className={style.stickyContainer}>
<div className={style.actions}>
{!disableFiltering && (
<Checkbox
className={style.verboseCheckbox}
onChange={handleVerboseFilterChange}
label={m.verboseStream}
value={!Boolean(filter)}
name="verbose-stream"
/>
)}
<Button
onClick={onPause}
message={paused ? sharedMessages.resume : sharedMessages.pause}
Expand Down Expand Up @@ -125,19 +151,25 @@ const Events = React.memo(
)

Events.propTypes = {
disableFiltering: PropTypes.bool,
entityId: PropTypes.string.isRequired,
events: PropTypes.events.isRequired,
filter: PropTypes.string,
onClear: PropTypes.func,
onFilterChange: PropTypes.func,
onPauseToggle: PropTypes.func,
paused: PropTypes.bool.isRequired,
scoped: PropTypes.bool,
truncated: PropTypes.bool.isRequired,
}

Events.defaultProps = {
disableFiltering: false,
filter: undefined,
scoped: false,
onClear: () => null,
onPauseToggle: () => null,
onFilterChange: () => null,
}

Events.Widget = Widget
Expand Down
1 change: 1 addition & 0 deletions pkg/webui/console/components/events/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const messages = defineMessages({
eventsTruncated:
'Old events have been truncated to save memory. The current event limit per stream is {limit}.',
eventUnavailable: 'This event is not available anymore. It was likely truncated to save memory.',
verboseStream: 'Verbose stream',
})

export default messages
49 changes: 49 additions & 0 deletions pkg/webui/console/constants/event-filters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright © 2021 The Things Network Foundation, The Things Industries B.V.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

export const EVENT_VERBOSE_FILTERS = [
'as.*.drop',
'as.down.data.forward',
'as.up.data.forward',
'gs.down.send',
'gs.gateway.connect',
'gs.gateway.disconnect',
'gs.status.receive',
'gs.up.receive',
'js.join.accept',
'js.join.reject',
'ns.mac.*.answer.reject',
'*.warning',
'*.fail',
'organization.*',
'user.*',
'gateway.*',
'application.*',
'end_device.*',
'client.*',
'oauth.*',
]

// A RegExp converted from the glob list of filtered event names.
export const EVENT_VERBOSE_FILTERS_REGEXP = EVENT_VERBOSE_FILTERS.reduce(
(acc, cur, i) => `${acc}${i !== 0 ? '|' : ''}${cur.replace('.', '\\.').replace('*', '.*')}`,
'',
)

// A map that allows to translate back the filter list from the converted
// RegExp string. Useful to show a human readable filter list in the event
// stream, which only uses the RegExp string internally.
export const EVENT_FILTER_MAP = Object.freeze({
[EVENT_VERBOSE_FILTERS_REGEXP]: EVENT_VERBOSE_FILTERS,
})
21 changes: 20 additions & 1 deletion pkg/webui/console/containers/application-events/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,28 @@ import {
clearApplicationEventsStream,
pauseApplicationEventsStream,
resumeApplicationEventsStream,
setApplicationEventsFilter,
} from '@console/store/actions/applications'

import {
selectApplicationEvents,
selectApplicationEventsPaused,
selectApplicationEventsTruncated,
selectApplicationEventsFilter,
} from '@console/store/selectors/applications'

const ApplicationEvents = props => {
const { appId, events, widget, paused, onClear, onPauseToggle, truncated } = props
const {
appId,
events,
widget,
paused,
onClear,
onPauseToggle,
truncated,
onFilterChange,
filter,
} = props

if (widget) {
return (
Expand All @@ -51,15 +63,19 @@ const ApplicationEvents = props => {
paused={paused}
onClear={onClear}
truncated={truncated}
filter={filter}
onPauseToggle={onPauseToggle}
onFilterChange={onFilterChange}
/>
)
}

ApplicationEvents.propTypes = {
appId: PropTypes.string.isRequired,
events: PropTypes.events,
filter: PropTypes.string,
onClear: PropTypes.func.isRequired,
onFilterChange: PropTypes.func.isRequired,
onPauseToggle: PropTypes.func.isRequired,
paused: PropTypes.bool.isRequired,
truncated: PropTypes.bool.isRequired,
Expand All @@ -69,6 +85,7 @@ ApplicationEvents.propTypes = {
ApplicationEvents.defaultProps = {
widget: false,
events: [],
filter: undefined,
}

export default withFeatureRequirement(mayViewApplicationEvents)(
Expand All @@ -80,6 +97,7 @@ export default withFeatureRequirement(mayViewApplicationEvents)(
events: selectApplicationEvents(state, appId),
paused: selectApplicationEventsPaused(state, appId),
truncated: selectApplicationEventsTruncated(state, appId),
filter: selectApplicationEventsFilter(state, appId),
}
},
(dispatch, ownProps) => ({
Expand All @@ -88,6 +106,7 @@ export default withFeatureRequirement(mayViewApplicationEvents)(
paused
? dispatch(resumeApplicationEventsStream(ownProps.appId))
: dispatch(pauseApplicationEventsStream(ownProps.appId)),
onFilterChange: filter => dispatch(setApplicationEventsFilter(ownProps.appId, filter)),
}),
)(ApplicationEvents),
)
22 changes: 21 additions & 1 deletion pkg/webui/console/containers/device-events/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,29 @@ import {
clearDeviceEventsStream,
pauseDeviceEventsStream,
resumeDeviceEventsStream,
setDeviceEventsFilter,
} from '@console/store/actions/devices'

import {
selectDeviceEvents,
selectDeviceEventsPaused,
selectDeviceEventsTruncated,
selectDeviceEventsFilter,
} from '@console/store/selectors/devices'

const DeviceEvents = props => {
const { appId, devId, events, widget, paused, onClear, onPauseToggle, truncated } = props
const {
appId,
devId,
events,
widget,
paused,
onClear,
onPauseToggle,
onFilterChange,
truncated,
filter,
} = props

if (widget) {
return (
Expand All @@ -51,8 +64,10 @@ const DeviceEvents = props => {
events={events}
entityId={devId}
paused={paused}
filter={filter}
onClear={onClear}
onPauseToggle={onPauseToggle}
onFilterChange={onFilterChange}
truncated={truncated}
scoped
widget
Expand All @@ -70,7 +85,9 @@ DeviceEvents.propTypes = {
}),
}).isRequired,
events: PropTypes.events,
filter: PropTypes.string,
onClear: PropTypes.func.isRequired,
onFilterChange: PropTypes.func.isRequired,
onPauseToggle: PropTypes.func.isRequired,
paused: PropTypes.bool.isRequired,
truncated: PropTypes.bool.isRequired,
Expand All @@ -80,6 +97,7 @@ DeviceEvents.propTypes = {
DeviceEvents.defaultProps = {
widget: false,
events: [],
filter: undefined,
}

export default connect(
Expand All @@ -96,6 +114,7 @@ export default connect(
events: selectDeviceEvents(state, combinedId),
paused: selectDeviceEventsPaused(state, combinedId),
truncated: selectDeviceEventsTruncated(state, combinedId),
filter: selectDeviceEventsFilter(state, combinedId),
}
},
(dispatch, ownProps) => {
Expand All @@ -107,6 +126,7 @@ export default connect(
paused
? dispatch(resumeDeviceEventsStream(devIds))
: dispatch(pauseDeviceEventsStream(devIds)),
onFilterChange: filter => dispatch(setDeviceEventsFilter(devIds, filter)),
}
},
)(DeviceEvents)
21 changes: 20 additions & 1 deletion pkg/webui/console/containers/gateway-events/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,28 @@ import {
clearGatewayEventsStream,
pauseGatewayEventsStream,
resumeGatewayEventsStream,
setGatewayEventsFilter,
} from '@console/store/actions/gateways'

import {
selectGatewayEvents,
selectGatewayEventsPaused,
selectGatewayEventsTruncated,
selectGatewayEventsFilter,
} from '@console/store/selectors/gateways'

const GatewayEvents = props => {
const { gtwId, events, widget, paused, onPauseToggle, onClear, truncated } = props
const {
gtwId,
events,
widget,
paused,
onPauseToggle,
onClear,
onFilterChange,
truncated,
filter,
} = props

if (widget) {
return (
Expand All @@ -51,16 +63,20 @@ const GatewayEvents = props => {
paused={paused}
onClear={onClear}
onPauseToggle={onPauseToggle}
onFilterChange={onFilterChange}
truncated={truncated}
filter={filter}
scoped
/>
)
}

GatewayEvents.propTypes = {
events: PropTypes.events,
filter: PropTypes.string,
gtwId: PropTypes.string.isRequired,
onClear: PropTypes.func.isRequired,
onFilterChange: PropTypes.func.isRequired,
onPauseToggle: PropTypes.func.isRequired,
paused: PropTypes.bool.isRequired,
truncated: PropTypes.bool.isRequired,
Expand All @@ -70,6 +86,7 @@ GatewayEvents.propTypes = {
GatewayEvents.defaultProps = {
widget: false,
events: [],
filter: undefined,
}

export default withFeatureRequirement(mayViewGatewayEvents)(
Expand All @@ -81,6 +98,7 @@ export default withFeatureRequirement(mayViewGatewayEvents)(
events: selectGatewayEvents(state, gtwId),
paused: selectGatewayEventsPaused(state, gtwId),
truncated: selectGatewayEventsTruncated(state, gtwId),
filter: selectGatewayEventsFilter(state, gtwId),
}
},
(dispatch, ownProps) => ({
Expand All @@ -89,6 +107,7 @@ export default withFeatureRequirement(mayViewGatewayEvents)(
paused
? dispatch(resumeGatewayEventsStream(ownProps.gtwId))
: dispatch(pauseGatewayEventsStream(ownProps.gtwId)),
onFilterChange: filter => dispatch(setGatewayEventsFilter(ownProps.gtwId, filter)),
}),
)(GatewayEvents),
)
Loading

0 comments on commit 646c4dd

Please sign in to comment.