feat: add check tags (#14700)

* Enable getChecks

* Fix EditCheck check getting

* Fix threshold null bugs

* Add CheckTagSet editing

* Remove console.log
pull/14712/head
Deniz Kusefoglu 2019-08-19 10:25:00 -07:00 committed by GitHub
parent 86cca67de1
commit b90cfc91e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 161 additions and 127 deletions

View File

@ -15,11 +15,15 @@ import {
notify,
Action as NotificationAction,
} from 'src/shared/actions/notifications'
import {Action as TimeMachineAction} from 'src/timeMachine/actions'
import {setCheckStatus, setTimeMachineCheck} from 'src/timeMachine/actions'
import {
Action as TimeMachineAction,
setActiveTimeMachine,
} from 'src/timeMachine/actions'
import {setCheckStatus} from 'src/timeMachine/actions'
// Types
import {Check, GetState, RemoteDataState} from 'src/types'
import {Check, GetState, RemoteDataState, CheckViewProperties} from 'src/types'
import {createView} from 'src/shared/utils/view'
export type Action =
| ReturnType<typeof setAllChecks>
@ -42,25 +46,24 @@ export const removeCheck = (checkID: string) => ({
})
export const getChecks = () => async (
dispatch: Dispatch<Action | NotificationAction>
// getState: GetState
dispatch: Dispatch<Action | NotificationAction>,
getState: GetState
) => {
try {
dispatch(setAllChecks(RemoteDataState.Loading))
// TODO: use this when its actually implemented
// const {
// orgs: {
// org: {id: orgID},
// },
// } = getState()
const {
orgs: {
org: {id: orgID},
},
} = getState()
// const resp = await api.getChecks({query: {orgID}})
const resp = await api.getChecks({query: {orgID}})
// if (resp.status !== 200) {
// throw new Error(resp.data.message)
// }
if (resp.status !== 200) {
throw new Error(resp.data.message)
}
dispatch(setAllChecks(RemoteDataState.Done, []))
dispatch(setAllChecks(RemoteDataState.Done, resp.data.checks))
} catch (e) {
console.error(e)
dispatch(setAllChecks(RemoteDataState.Error))
@ -80,7 +83,15 @@ export const getCheckForTimeMachine = (checkID: string) => async (
throw new Error(resp.data.message)
}
dispatch(setTimeMachineCheck(RemoteDataState.Done, resp.data))
const view = createView<CheckViewProperties>('check')
// todo: when check has own view get view here
dispatch(
setActiveTimeMachine('alerting', {
view,
activeTab: 'alerting',
alerting: {check: resp.data, checkStatus: RemoteDataState.Done},
})
)
} catch (e) {
console.error(e)
dispatch(setCheckStatus(RemoteDataState.Error))

View File

@ -9,7 +9,6 @@ import CheckEOHeader from 'src/alerting/components/CheckEOHeader'
import TimeMachine from 'src/timeMachine/components/TimeMachine'
// Utils
import {createView} from 'src/shared/utils/view'
import {getActiveTimeMachine} from 'src/timeMachine/selectors'
// Actions
@ -26,7 +25,6 @@ import {
AppState,
RemoteDataState,
DashboardDraftQuery,
CheckViewProperties,
TimeMachineID,
} from 'src/types'
@ -51,7 +49,6 @@ const EditCheckEditorOverlay: FunctionComponent<Props> = ({
onUpdateCheck,
onGetCheckForTimeMachine,
onUpdateTimeMachineCheck,
onSetActiveTimeMachine,
onSetTimeMachineCheck,
activeTimeMachineID,
checkStatus,
@ -61,26 +58,16 @@ const EditCheckEditorOverlay: FunctionComponent<Props> = ({
check,
}) => {
useEffect(() => {
if (check) {
const view = createView<CheckViewProperties>('check')
// todo: when check has own view get view here
onSetActiveTimeMachine('alerting', {
view,
activeTab: 'alerting',
isViewingRawData: false,
})
} else {
onGetCheckForTimeMachine(checkID)
}
}, [check, checkID])
onGetCheckForTimeMachine(checkID)
}, [checkID])
const handleUpdateName = (name: string) => {
onUpdateTimeMachineCheck({name})
}
const handleClose = () => {
onSetTimeMachineCheck(RemoteDataState.NotStarted, null)
router.push(`/orgs/${orgID}/alerting`)
onSetTimeMachineCheck(RemoteDataState.NotStarted, null)
}
const handleSave = () => {

View File

@ -1,7 +1,6 @@
// Libraries
import React, {FC, ChangeEvent} from 'react'
import React, {FC} from 'react'
import {connect} from 'react-redux'
import {get} from 'lodash'
// Components
import {
@ -13,8 +12,12 @@ import {
TextArea,
AutoComplete,
Wrap,
ComponentColor,
Grid,
} from '@influxdata/clockface'
import {Input} from '@influxdata/clockface'
import DashedButton from 'src/shared/components/dashed_button/DashedButton'
import CheckTagRow from 'src/alerting/components/builder/CheckTagRow'
// Actions
import {updateTimeMachineCheck, changeCheckType} from 'src/timeMachine/actions'
@ -23,7 +26,7 @@ import {updateTimeMachineCheck, changeCheckType} from 'src/timeMachine/actions'
import {getActiveTimeMachine} from 'src/timeMachine/selectors'
// Types
import {Check, AppState, CheckType} from 'src/types'
import {Check, AppState, CheckType, CheckTagSet} from 'src/types'
import {
DEFAULT_CHECK_EVERY,
DEFAULT_CHECK_OFFSET,
@ -50,24 +53,15 @@ const CheckMetaCard: FC<Props> = ({
changeCheckType(type)
}
const handleChangeName = (e: React.ChangeEvent<HTMLInputElement>) => {
updateTimeMachineCheck({name: e.target.value})
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
updateTimeMachineCheck({[e.target.name]: e.target.value})
}
const handleChangeKey = (e: React.ChangeEvent<HTMLInputElement>) => {
updateTimeMachineCheck({
tags: [{key: e.target.value, value: get(check, 'tags[0].value', '')}],
})
}
const handleChangeValue = (e: React.ChangeEvent<HTMLInputElement>) => {
updateTimeMachineCheck({
tags: [{value: e.target.value, key: get(check, 'tags[0].key', '')}],
})
}
const handleChangeMessage = (e: ChangeEvent<HTMLTextAreaElement>) => {
const statusMessageTemplate = e.target.value
updateTimeMachineCheck({statusMessageTemplate})
const addTagsRow = () => {
const tags = check.tags || []
updateTimeMachineCheck({tags: [...tags, {key: '', value: ''}]})
}
const handleChangeSchedule = (scheduleType: 'cron' | 'every') => {
@ -88,6 +82,12 @@ const CheckMetaCard: FC<Props> = ({
return
}
}
const handleChangeTagRow = (index: number, tagSet: CheckTagSet) => {
const tags = [...check.tags]
tags[index] = tagSet
updateTimeMachineCheck({tags})
}
return (
<>
<Form.Element label="Check Type">
@ -117,15 +117,10 @@ const CheckMetaCard: FC<Props> = ({
<Form.Element label="Name">
<Input
autoFocus={true}
maxLength={24}
name="Name"
onChange={handleChangeName}
name="name"
onChange={handleChange}
placeholder="Name this check"
size={ComponentSize.Small}
spellCheck={false}
testID="input-field"
titleText="Title Text"
type={InputType.Text}
value={check.name}
/>
</Form.Element>
@ -139,8 +134,8 @@ const CheckMetaCard: FC<Props> = ({
form=""
maxLength={50}
minLength={5}
name=""
onChange={handleChangeMessage}
name="statusMessageTemplate"
onChange={handleChange}
placeholder="Example: {tags.cpu} exceeded threshold: {value}%"
readOnly={false}
required={false}
@ -151,30 +146,6 @@ const CheckMetaCard: FC<Props> = ({
wrap={Wrap.Soft}
/>
</Form.Element>
<Form.Element label="Tag-Key">
<Input
maxLength={24}
name="TagKey"
onChange={handleChangeKey}
placeholder="Key"
size={ComponentSize.Small}
spellCheck={false}
testID="input-field"
value={get(check, 'tags[0].key', '')}
/>
</Form.Element>
<Form.Element label="Tag-Value">
<Input
maxLength={24}
name="TagValue"
onChange={handleChangeValue}
placeholder="Value"
size={ComponentSize.Small}
spellCheck={false}
testID="input-field"
value={get(check, 'tags[0].value', '')}
/>
</Form.Element>
<Form.Element label="Schedule">
<Radio shape={ButtonShape.StretchToFit}>
<Radio.Button
@ -199,47 +170,41 @@ const CheckMetaCard: FC<Props> = ({
</Radio.Button>
</Radio>
</Form.Element>
{check.every != null && (
<Form.Element label="Every">
<Input
autoFocus={false}
maxLength={24}
name="Name"
onChange={handleChangeName}
placeholder="Name this check"
size={ComponentSize.Small}
spellCheck={false}
testID="input-field"
titleText="Name of the check"
type={InputType.Text}
value={check.every}
/>
</Form.Element>
)}
{check.offset != null && (
<Form.Element label="Offset">
<Input
autoFocus={false}
maxLength={24}
name="Offset"
onChange={handleChangeName}
placeholder="offset"
size={ComponentSize.Small}
spellCheck={false}
testID="input-field"
titleText="Offset check interval"
type={InputType.Text}
value={check.offset}
/>
</Form.Element>
)}
<Grid>
<Grid.Row>
<Grid.Column widthSM={6}>
{check.every != null && (
<Form.Element label="Every">
<Input
name="every"
onChange={handleChange}
titleText="Name of the check"
value={check.every}
/>
</Form.Element>
)}
</Grid.Column>
<Grid.Column widthSM={6}>
{check.offset != null && (
<Form.Element label="Offset">
<Input
name="offset"
onChange={handleChange}
titleText="Offset check interval"
value={check.offset}
/>
</Form.Element>
)}
</Grid.Column>
</Grid.Row>
</Grid>
{check.cron != null && (
<Form.Element label="Cron">
<Input
autoFocus={false}
maxLength={24}
name="Cron"
onChange={handleChangeName}
name="cron"
onChange={handleChange}
placeholder="cron"
size={ComponentSize.Small}
spellCheck={false}
@ -250,6 +215,21 @@ const CheckMetaCard: FC<Props> = ({
/>
</Form.Element>
)}
{check.tags &&
check.tags.map((t, i) => (
<CheckTagRow
key={i}
index={i}
tagSet={t}
handleChangeTagRow={handleChangeTagRow}
/>
))}
<DashedButton
text="+ Tags"
onClick={addTagsRow}
color={ComponentColor.Primary}
size={ComponentSize.Small}
/>
</>
)
}

View File

@ -0,0 +1,48 @@
// Libraries
import React, {FC} from 'react'
// Components
import {Grid, Input, Form} from '@influxdata/clockface'
// Types
import {CheckTagSet} from 'src/types'
interface Props {
index: number
tagSet: CheckTagSet
handleChangeTagRow: (i: number, tags: CheckTagSet) => void
}
const CheckTagRow: FC<Props> = ({tagSet, handleChangeTagRow, index}) => {
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
handleChangeTagRow(index, {...tagSet, [e.target.name]: e.target.value})
}
return (
<Grid>
<Grid.Row>
<Grid.Column widthSM={6}>
<Form.Element label="Tag Key">
<Input
name="key"
onChange={handleChange}
titleText="Name of the check"
value={tagSet.key}
/>
</Form.Element>
</Grid.Column>
<Grid.Column widthSM={6}>
<Form.Element label="Tag Value">
<Input
name="value"
onChange={handleChange}
titleText="Offset check interval"
value={tagSet.value}
/>
</Form.Element>
</Grid.Column>
</Grid.Row>
</Grid>
)
}
export default CheckTagRow

View File

@ -19,9 +19,11 @@ interface Props {
const ThresholdConditions: FC<Props> = ({check}) => {
const thresholds = {}
check.thresholds.forEach(t => {
thresholds[t.level] = t
})
if (check.thresholds) {
check.thresholds.forEach(t => {
thresholds[t.level] = t
})
}
return (
<FlexBox
direction={FlexDirection.Column}

View File

@ -913,13 +913,17 @@ export const timeMachineReducer = (
return produce(state, draftState => {
const check = draftState.alerting.check
if (check.type === 'threshold') {
const filteredThresholds = check.thresholds.filter(
const thresholds = check.thresholds || []
const filteredThresholds = thresholds.filter(
t => t.level !== action.payload.threshold.level
)
const thresholds = [...filteredThresholds, action.payload.threshold]
const updatedThresholds = [
...filteredThresholds,
action.payload.threshold,
]
draftState.alerting.check = {
...check,
thresholds,
thresholds: updatedThresholds,
}
}
})

View File

@ -74,4 +74,6 @@ import {Check, Threshold} from '../client'
export type CheckType = Check['type']
export type ThresholdType = Threshold['type']
export type CheckTagSet = Check['tags'][0]
export type AlertHistoryType = 'statuses' | 'notifications'