Add alerting cards (#14429)
* bump to client 0.5.3 * export Query type from client and create alerting mock * Bump client to 0.5.5 * Merge all status enums to one type * Fix threshold visualization types * Add extra underscore to notification rules * Use check view type from client * Add description field to checks, notificationrules and endpoints * Add init check cards * Add check card actions * Correct component name * Add check card context and toggle * Add id to notification rule base * Add notification rule cards * add checks reducer tests * user immer for checks reducer * add tests and immer to notification rule reducerpull/14433/head
parent
da4615ea17
commit
613aef698f
|
@ -6497,12 +6497,7 @@ components:
|
||||||
description: An optional description of the task.
|
description: An optional description of the task.
|
||||||
type: string
|
type: string
|
||||||
status:
|
status:
|
||||||
description: The current status of the task. When updated to 'inactive', cancels all queued jobs of this task.
|
$ref: "#/components/schemas/TaskStatusType"
|
||||||
default: active
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- active
|
|
||||||
- inactive
|
|
||||||
labels:
|
labels:
|
||||||
$ref: "#/components/schemas/Labels"
|
$ref: "#/components/schemas/Labels"
|
||||||
authorizationID:
|
authorizationID:
|
||||||
|
@ -6557,6 +6552,9 @@ components:
|
||||||
labels:
|
labels:
|
||||||
$ref: "#/components/schemas/Link"
|
$ref: "#/components/schemas/Link"
|
||||||
required: [id, name, orgID, flux]
|
required: [id, name, orgID, flux]
|
||||||
|
TaskStatusType:
|
||||||
|
type: string
|
||||||
|
enum: [active, inactive]
|
||||||
User:
|
User:
|
||||||
properties:
|
properties:
|
||||||
id:
|
id:
|
||||||
|
@ -8684,10 +8682,7 @@ components:
|
||||||
query:
|
query:
|
||||||
$ref: "#/components/schemas/DashboardQuery"
|
$ref: "#/components/schemas/DashboardQuery"
|
||||||
status:
|
status:
|
||||||
description: The status of the check task.
|
$ref: "#/components/schemas/TaskStatusType"
|
||||||
default: active
|
|
||||||
type: string
|
|
||||||
enum: ["active", "inactive"]
|
|
||||||
every:
|
every:
|
||||||
description: Check repetition interval
|
description: Check repetition interval
|
||||||
type: string
|
type: string
|
||||||
|
@ -8707,6 +8702,9 @@ components:
|
||||||
type: string
|
type: string
|
||||||
value:
|
value:
|
||||||
type: string
|
type: string
|
||||||
|
description:
|
||||||
|
description: An optional description of the check
|
||||||
|
type: string
|
||||||
statusMessageTemplate:
|
statusMessageTemplate:
|
||||||
description: template that is used to generate and write a status message
|
description: template that is used to generate and write a status message
|
||||||
type: string
|
type: string
|
||||||
|
@ -8812,6 +8810,9 @@ components:
|
||||||
NotificationRuleBase:
|
NotificationRuleBase:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
id:
|
||||||
|
readOnly: true
|
||||||
|
type: string
|
||||||
notifyEndpointID:
|
notifyEndpointID:
|
||||||
type: string
|
type: string
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
@ -8831,10 +8832,7 @@ components:
|
||||||
format: date-time
|
format: date-time
|
||||||
readOnly: true
|
readOnly: true
|
||||||
status:
|
status:
|
||||||
description: The status of the notification rule task.
|
$ref: "#/components/schemas/TaskStatusType"
|
||||||
default: active
|
|
||||||
type: string
|
|
||||||
enum: ["active", "inactive"]
|
|
||||||
name:
|
name:
|
||||||
description: human-readable name describing the notification rule
|
description: human-readable name describing the notification rule
|
||||||
type: string
|
type: string
|
||||||
|
@ -8864,6 +8862,9 @@ components:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/TagRule"
|
$ref: "#/components/schemas/TagRule"
|
||||||
|
description:
|
||||||
|
description: An optional description of the notification rule
|
||||||
|
type: string
|
||||||
statusRules:
|
statusRules:
|
||||||
description: list of status rules the notification rule attempts to match
|
description: list of status rules the notification rule attempts to match
|
||||||
type: array
|
type: array
|
||||||
|
@ -8970,6 +8971,9 @@ components:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
description:
|
||||||
|
description: An optional description of the notification endpoint
|
||||||
|
type: string
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
status:
|
status:
|
||||||
|
|
|
@ -157,7 +157,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@influxdata/clockface": "0.0.13",
|
"@influxdata/clockface": "0.0.13",
|
||||||
"@influxdata/giraffe": "0.16.0",
|
"@influxdata/giraffe": "0.16.0",
|
||||||
"@influxdata/influx": "0.5.0",
|
"@influxdata/influx": "0.5.5",
|
||||||
"@influxdata/influxdb-templates": "0.3.0",
|
"@influxdata/influxdb-templates": "0.3.0",
|
||||||
"@influxdata/react-custom-scrollbars": "4.3.8",
|
"@influxdata/react-custom-scrollbars": "4.3.8",
|
||||||
"axios": "^0.19.0",
|
"axios": "^0.19.0",
|
||||||
|
|
|
@ -17,28 +17,28 @@ import {Check, GetState} from 'src/types'
|
||||||
|
|
||||||
export type Action =
|
export type Action =
|
||||||
| ReturnType<typeof setAllChecks>
|
| ReturnType<typeof setAllChecks>
|
||||||
| ReturnType<typeof setChecksStatus>
|
|
||||||
| ReturnType<typeof setCheck>
|
| ReturnType<typeof setCheck>
|
||||||
| ReturnType<typeof setCheckStatus>
|
| ReturnType<typeof removeCheck>
|
||||||
|
| ReturnType<typeof setCurrentCheck>
|
||||||
|
|
||||||
const setAllChecks = (status: RemoteDataState, checks?: Check[]) => ({
|
export const setAllChecks = (status: RemoteDataState, checks?: Check[]) => ({
|
||||||
type: 'SET_ALL_CHECKS' as 'SET_ALL_CHECKS',
|
type: 'SET_ALL_CHECKS' as 'SET_ALL_CHECKS',
|
||||||
payload: {status, checks},
|
payload: {status, checks},
|
||||||
})
|
})
|
||||||
|
|
||||||
const setChecksStatus = (status: RemoteDataState) => ({
|
export const setCheck = (check: Check) => ({
|
||||||
type: 'SET_CHECKS_STATUS' as 'SET_CHECKS_STATUS',
|
|
||||||
payload: {status},
|
|
||||||
})
|
|
||||||
|
|
||||||
const setCheck = (status: RemoteDataState, check?: Check) => ({
|
|
||||||
type: 'SET_CHECK' as 'SET_CHECK',
|
type: 'SET_CHECK' as 'SET_CHECK',
|
||||||
payload: {status, check},
|
payload: {check},
|
||||||
})
|
})
|
||||||
|
|
||||||
const setCheckStatus = (status: RemoteDataState) => ({
|
export const removeCheck = (checkID: string) => ({
|
||||||
type: 'SET_CHECK_STATUS' as 'SET_CHECK_STATUS',
|
type: 'REMOVE_CHECK' as 'REMOVE_CHECK',
|
||||||
payload: {status},
|
payload: {checkID},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const setCurrentCheck = (status: RemoteDataState, check?: Check) => ({
|
||||||
|
type: 'SET_CURRENT_CHECK' as 'SET_CURRENT_CHECK',
|
||||||
|
payload: {status, check},
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getChecks = () => async (
|
export const getChecks = () => async (
|
||||||
|
@ -46,7 +46,7 @@ export const getChecks = () => async (
|
||||||
getState: GetState
|
getState: GetState
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
dispatch(setChecksStatus(RemoteDataState.Loading))
|
dispatch(setAllChecks(RemoteDataState.Loading))
|
||||||
const {
|
const {
|
||||||
orgs: {
|
orgs: {
|
||||||
org: {id: orgID},
|
org: {id: orgID},
|
||||||
|
@ -58,23 +58,58 @@ export const getChecks = () => async (
|
||||||
dispatch(setAllChecks(RemoteDataState.Done, checks))
|
dispatch(setAllChecks(RemoteDataState.Done, checks))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
dispatch(setChecksStatus(RemoteDataState.Error))
|
dispatch(setAllChecks(RemoteDataState.Error))
|
||||||
dispatch(notify(copy.getChecksFailed(e.message)))
|
dispatch(notify(copy.getChecksFailed(e.message)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getCheck = (checkID: string) => async (
|
export const getCurrentCheck = (checkID: string) => async (
|
||||||
dispatch: Dispatch<Action | NotificationAction>
|
dispatch: Dispatch<Action | NotificationAction>
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
dispatch(setCheckStatus(RemoteDataState.Loading))
|
dispatch(setCurrentCheck(RemoteDataState.Loading))
|
||||||
|
|
||||||
const check = await client.checks.get(checkID)
|
const check = await client.checks.get(checkID)
|
||||||
|
|
||||||
dispatch(setCheck(RemoteDataState.Done, check))
|
dispatch(setCurrentCheck(RemoteDataState.Done, check))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
dispatch(setCheckStatus(RemoteDataState.Error))
|
dispatch(setCurrentCheck(RemoteDataState.Error))
|
||||||
dispatch(notify(copy.getCheckFailed(e.message)))
|
dispatch(notify(copy.getCheckFailed(e.message)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const createCheck = (check: Check) => async (
|
||||||
|
dispatch: Dispatch<Action | NotificationAction>
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
client.checks.create(check)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
dispatch(notify(copy.createCheckFailed(e.message)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateCheck = (check: Partial<Check>) => async (
|
||||||
|
dispatch: Dispatch<Action | NotificationAction>
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const updatedCheck = await client.checks.update(check.id, check)
|
||||||
|
dispatch(setCheck(updatedCheck))
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
dispatch(notify(copy.updateCheckFailed(e.message)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteCheck = (checkID: string) => async (
|
||||||
|
dispatch: Dispatch<Action | NotificationAction>
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
await client.checks.delete(checkID)
|
||||||
|
dispatch(removeCheck(checkID))
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
dispatch(notify(copy.deleteCheckFailed(e.message)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {Dispatch} from 'react'
|
||||||
// Constants
|
// Constants
|
||||||
import * as copy from 'src/shared/copy/notifications'
|
import * as copy from 'src/shared/copy/notifications'
|
||||||
|
|
||||||
//Actions
|
// Actions
|
||||||
import {
|
import {
|
||||||
notify,
|
notify,
|
||||||
Action as NotificationAction,
|
Action as NotificationAction,
|
||||||
|
@ -17,34 +17,34 @@ import {NotificationRule, GetState} from 'src/types'
|
||||||
|
|
||||||
export type Action =
|
export type Action =
|
||||||
| ReturnType<typeof setAllNotificationRules>
|
| ReturnType<typeof setAllNotificationRules>
|
||||||
| ReturnType<typeof setNotificationRulesStatus>
|
|
||||||
| ReturnType<typeof setNotificationRule>
|
| ReturnType<typeof setNotificationRule>
|
||||||
| ReturnType<typeof setNotificationRuleStatus>
|
| ReturnType<typeof setCurrentNotificationRule>
|
||||||
|
| ReturnType<typeof removeNotificationRule>
|
||||||
|
|
||||||
const setAllNotificationRules = (
|
export const setAllNotificationRules = (
|
||||||
status: RemoteDataState,
|
status: RemoteDataState,
|
||||||
notificationRules?: NotificationRule[]
|
notificationRules?: NotificationRule[]
|
||||||
) => ({
|
) => ({
|
||||||
type: 'SET_ALL_NOTIFICATIONRULES' as 'SET_ALL_NOTIFICATIONRULES',
|
type: 'SET_ALL_NOTIFICATION_RULES' as 'SET_ALL_NOTIFICATION_RULES',
|
||||||
payload: {status, notificationRules},
|
payload: {status, notificationRules},
|
||||||
})
|
})
|
||||||
|
|
||||||
const setNotificationRulesStatus = (status: RemoteDataState) => ({
|
export const setNotificationRule = (notificationRule: NotificationRule) => ({
|
||||||
type: 'SET_NOTIFICATIONRULES_STATUS' as 'SET_NOTIFICATIONRULES_STATUS',
|
type: 'SET_NOTIFICATION_RULE' as 'SET_NOTIFICATION_RULE',
|
||||||
payload: {status},
|
payload: {notificationRule},
|
||||||
})
|
})
|
||||||
|
|
||||||
const setNotificationRule = (
|
export const setCurrentNotificationRule = (
|
||||||
status: RemoteDataState,
|
status: RemoteDataState,
|
||||||
notificationRule?: NotificationRule
|
notificationRule?: NotificationRule
|
||||||
) => ({
|
) => ({
|
||||||
type: 'SET_NOTIFICATIONRULE' as 'SET_NOTIFICATIONRULE',
|
type: 'SET_CURRENT_NOTIFICATION_RULE' as 'SET_CURRENT_NOTIFICATION_RULE',
|
||||||
payload: {status, notificationRule},
|
payload: {status, notificationRule},
|
||||||
})
|
})
|
||||||
|
|
||||||
const setNotificationRuleStatus = (status: RemoteDataState) => ({
|
export const removeNotificationRule = (notificationRuleID: string) => ({
|
||||||
type: 'SET_NOTIFICATIONRULE_STATUS' as 'SET_NOTIFICATIONRULE_STATUS',
|
type: 'REMOVE_NOTIFICATION_RULE' as 'REMOVE_NOTIFICATION_RULE',
|
||||||
payload: {status},
|
payload: {notificationRuleID},
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getNotificationRules = () => async (
|
export const getNotificationRules = () => async (
|
||||||
|
@ -52,37 +52,77 @@ export const getNotificationRules = () => async (
|
||||||
getState: GetState
|
getState: GetState
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
dispatch(setNotificationRulesStatus(RemoteDataState.Loading))
|
dispatch(setAllNotificationRules(RemoteDataState.Loading))
|
||||||
const {
|
const {
|
||||||
orgs: {
|
orgs: {
|
||||||
org: {id: orgID},
|
org: {id: orgID},
|
||||||
},
|
},
|
||||||
} = getState()
|
} = getState()
|
||||||
|
|
||||||
const notificationRules = await client.notificationRules.getAll(orgID)
|
const notificationRules = (await client.notificationRules.getAll(
|
||||||
|
orgID
|
||||||
|
)) as NotificationRule[]
|
||||||
|
|
||||||
dispatch(setAllNotificationRules(RemoteDataState.Done, notificationRules))
|
dispatch(setAllNotificationRules(RemoteDataState.Done, notificationRules))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
dispatch(setNotificationRulesStatus(RemoteDataState.Error))
|
dispatch(setAllNotificationRules(RemoteDataState.Error))
|
||||||
dispatch(notify(copy.getNotificationRulesFailed(e.message)))
|
dispatch(notify(copy.getNotificationRulesFailed(e.message)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getNotificationRule = (notificationRuleID: string) => async (
|
export const getCurrentNotificationRule = (
|
||||||
dispatch: Dispatch<Action | NotificationAction>
|
notificationRuleID: string
|
||||||
) => {
|
) => async (dispatch: Dispatch<Action | NotificationAction>) => {
|
||||||
try {
|
try {
|
||||||
dispatch(setNotificationRuleStatus(RemoteDataState.Loading))
|
dispatch(setCurrentNotificationRule(RemoteDataState.Loading))
|
||||||
|
|
||||||
const notificationRule = await client.notificationRules.get(
|
const notificationRule = (await client.notificationRules.get(
|
||||||
notificationRuleID
|
notificationRuleID
|
||||||
)
|
)) as NotificationRule
|
||||||
|
|
||||||
dispatch(setNotificationRule(RemoteDataState.Done, notificationRule))
|
dispatch(setCurrentNotificationRule(RemoteDataState.Done, notificationRule))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
dispatch(setNotificationRuleStatus(RemoteDataState.Error))
|
dispatch(setCurrentNotificationRule(RemoteDataState.Error))
|
||||||
dispatch(notify(copy.getNotificationRuleFailed(e.message)))
|
dispatch(notify(copy.getNotificationRuleFailed(e.message)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const createNotificationRule = (
|
||||||
|
notificationRule: NotificationRule
|
||||||
|
) => async (dispatch: Dispatch<Action | NotificationAction>) => {
|
||||||
|
try {
|
||||||
|
client.notificationRules.create(notificationRule)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
dispatch(notify(copy.createNotificationRuleFailed(e.message)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateNotificationRule = (
|
||||||
|
notificationRule: Partial<NotificationRule>
|
||||||
|
) => async (dispatch: Dispatch<Action | NotificationAction>) => {
|
||||||
|
try {
|
||||||
|
const updatedNotificationRule = (await client.notificationRules.update(
|
||||||
|
notificationRule.id,
|
||||||
|
notificationRule
|
||||||
|
)) as NotificationRule
|
||||||
|
dispatch(setNotificationRule(updatedNotificationRule))
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
dispatch(notify(copy.updateNotificationRuleFailed(e.message)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteNotificationRule = (notificationRuleID: string) => async (
|
||||||
|
dispatch: Dispatch<Action | NotificationAction>
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
await client.notificationRules.delete(notificationRuleID)
|
||||||
|
dispatch(removeNotificationRule(notificationRuleID))
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
dispatch(notify(copy.deleteNotificationRuleFailed(e.message)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
// Libraries
|
||||||
|
import React, {FunctionComponent} from 'react'
|
||||||
|
import {connect} from 'react-redux'
|
||||||
|
import {withRouter, WithRouterProps} from 'react-router'
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import {ResourceList} from 'src/clockface'
|
||||||
|
import {SlideToggle, ComponentSize} from '@influxdata/clockface'
|
||||||
|
import CheckCardContext from 'src/alerting/components/CheckCardContext'
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
import {DEFAULT_CHECK_NAME} from 'src/alerting/constants'
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
import {updateCheck, deleteCheck} from 'src/alerting/actions/checks'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import {Check, CheckBase} from 'src/types'
|
||||||
|
|
||||||
|
interface DispatchProps {
|
||||||
|
updateCheck: typeof updateCheck
|
||||||
|
deleteCheck: typeof deleteCheck
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OwnProps {
|
||||||
|
check: Check
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = OwnProps & DispatchProps & WithRouterProps
|
||||||
|
|
||||||
|
const CheckCard: FunctionComponent<Props> = ({
|
||||||
|
check,
|
||||||
|
updateCheck,
|
||||||
|
deleteCheck,
|
||||||
|
router,
|
||||||
|
params: {orgID},
|
||||||
|
}) => {
|
||||||
|
const onUpdateName = (name: string) => {
|
||||||
|
updateCheck({id: check.id, name})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClickName = () => {
|
||||||
|
router.push(`/orgs/${orgID}/checks/${check.id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDelete = () => {
|
||||||
|
deleteCheck(check.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onExport = () => {}
|
||||||
|
|
||||||
|
const onClone = () => {}
|
||||||
|
|
||||||
|
const onToggle = () => {
|
||||||
|
const status =
|
||||||
|
check.status == CheckBase.StatusEnum.Active
|
||||||
|
? CheckBase.StatusEnum.Inactive
|
||||||
|
: CheckBase.StatusEnum.Active
|
||||||
|
updateCheck({id: check.id, status})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ResourceList.Card
|
||||||
|
key={`check-id--${check.id}`}
|
||||||
|
testID="check-card"
|
||||||
|
name={() => (
|
||||||
|
<ResourceList.EditableName
|
||||||
|
onUpdate={onUpdateName}
|
||||||
|
onClick={onClickName}
|
||||||
|
name={check.name}
|
||||||
|
noNameString={DEFAULT_CHECK_NAME}
|
||||||
|
parentTestID="check-card--name"
|
||||||
|
buttonTestID="check-card--name-button"
|
||||||
|
inputTestID="check-card--input"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
toggle={() => (
|
||||||
|
<SlideToggle
|
||||||
|
active={check.status == CheckBase.StatusEnum.Active}
|
||||||
|
size={ComponentSize.ExtraSmall}
|
||||||
|
onChange={onToggle}
|
||||||
|
testID="check-card--slide-toggle"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
// description
|
||||||
|
// labels
|
||||||
|
disabled={check.status == CheckBase.StatusEnum.Inactive}
|
||||||
|
contextMenu={() => (
|
||||||
|
<CheckCardContext
|
||||||
|
onDelete={onDelete}
|
||||||
|
onExport={onExport}
|
||||||
|
onClone={onClone}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
updatedAt={check.updatedAt.toString()}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const mdtp: DispatchProps = {
|
||||||
|
updateCheck: updateCheck,
|
||||||
|
deleteCheck: deleteCheck,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect<{}, DispatchProps, {}>(
|
||||||
|
null,
|
||||||
|
mdtp
|
||||||
|
)(withRouter(CheckCard))
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Libraries
|
||||||
|
import React, {FunctionComponent} from 'react'
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import {Context, IconFont} from 'src/clockface'
|
||||||
|
import {ComponentColor} from '@influxdata/clockface'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
onDelete: () => void
|
||||||
|
onClone: () => void
|
||||||
|
onExport: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const CheckCardContext: FunctionComponent<Props> = ({
|
||||||
|
onDelete,
|
||||||
|
onClone,
|
||||||
|
onExport,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Context>
|
||||||
|
<Context.Menu icon={IconFont.CogThick}>
|
||||||
|
<Context.Item label="Export" action={onExport} />
|
||||||
|
</Context.Menu>
|
||||||
|
<Context.Menu icon={IconFont.Duplicate} color={ComponentColor.Secondary}>
|
||||||
|
<Context.Item label="Clone" action={onClone} />
|
||||||
|
</Context.Menu>
|
||||||
|
<Context.Menu
|
||||||
|
icon={IconFont.Trash}
|
||||||
|
color={ComponentColor.Danger}
|
||||||
|
testID="context-delete-menu"
|
||||||
|
>
|
||||||
|
<Context.Item
|
||||||
|
label="Delete"
|
||||||
|
action={onDelete}
|
||||||
|
testID="context-delete-task"
|
||||||
|
/>
|
||||||
|
</Context.Menu>
|
||||||
|
</Context>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CheckCardContext
|
|
@ -1,5 +1,10 @@
|
||||||
|
//Libraries
|
||||||
import React, {FunctionComponent} from 'react'
|
import React, {FunctionComponent} from 'react'
|
||||||
|
|
||||||
|
//Components
|
||||||
|
import CheckCard from 'src/alerting/components/CheckCard'
|
||||||
|
import {ResourceList} from 'src/clockface'
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import {Check} from 'src/types'
|
import {Check} from 'src/types'
|
||||||
import {EmptyState, ComponentSize} from '@influxdata/clockface'
|
import {EmptyState, ComponentSize} from '@influxdata/clockface'
|
||||||
|
@ -9,9 +14,20 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
const CheckCards: FunctionComponent<Props> = ({checks}) => {
|
const CheckCards: FunctionComponent<Props> = ({checks}) => {
|
||||||
if (checks && checks.length) {
|
return (
|
||||||
return <></>
|
<>
|
||||||
}
|
<ResourceList>
|
||||||
|
<ResourceList.Body emptyState={<EmptyChecksList />}>
|
||||||
|
{checks.map(check => (
|
||||||
|
<CheckCard key={check.id} check={check} />
|
||||||
|
))}
|
||||||
|
</ResourceList.Body>
|
||||||
|
</ResourceList>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const EmptyChecksList: FunctionComponent = () => {
|
||||||
return (
|
return (
|
||||||
<EmptyState size={ComponentSize.ExtraSmall}>
|
<EmptyState size={ComponentSize.ExtraSmall}>
|
||||||
<EmptyState.Text
|
<EmptyState.Text
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
// Libraries
|
||||||
|
import React, {FunctionComponent} from 'react'
|
||||||
|
import {connect} from 'react-redux'
|
||||||
|
import {withRouter, WithRouterProps} from 'react-router'
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import {ResourceList} from 'src/clockface'
|
||||||
|
import {SlideToggle, ComponentSize} from '@influxdata/clockface'
|
||||||
|
import NotificationRuleCardContext from 'src/alerting/components/NotificationRuleCardContext'
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
import {DEFAULT_NOTIFICATION_RULE_NAME} from 'src/alerting/constants'
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
import {
|
||||||
|
updateNotificationRule,
|
||||||
|
deleteNotificationRule,
|
||||||
|
} from 'src/alerting/actions/notificationRules'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import {NotificationRule, NotificationRuleBase} from 'src/types'
|
||||||
|
|
||||||
|
interface DispatchProps {
|
||||||
|
updateNotificationRule: typeof updateNotificationRule
|
||||||
|
deleteNotificationRule: typeof deleteNotificationRule
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OwnProps {
|
||||||
|
notificationRule: NotificationRule
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = OwnProps & DispatchProps & WithRouterProps
|
||||||
|
|
||||||
|
const NotificationRuleCard: FunctionComponent<Props> = ({
|
||||||
|
notificationRule,
|
||||||
|
updateNotificationRule,
|
||||||
|
deleteNotificationRule,
|
||||||
|
router,
|
||||||
|
params: {orgID},
|
||||||
|
}) => {
|
||||||
|
const onUpdateName = (name: string) => {
|
||||||
|
updateNotificationRule({id: notificationRule.id, name})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClickName = () => {
|
||||||
|
router.push(`/orgs/${orgID}/notificationRules/${notificationRule.id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDelete = () => {
|
||||||
|
deleteNotificationRule(notificationRule.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onExport = () => {}
|
||||||
|
|
||||||
|
const onClone = () => {}
|
||||||
|
|
||||||
|
const onToggle = () => {
|
||||||
|
const status =
|
||||||
|
notificationRule.status == NotificationRuleBase.StatusEnum.Active
|
||||||
|
? NotificationRuleBase.StatusEnum.Inactive
|
||||||
|
: NotificationRuleBase.StatusEnum.Active
|
||||||
|
updateNotificationRule({id: notificationRule.id, status})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ResourceList.Card
|
||||||
|
key={`notificationRule-id--${notificationRule.id}`}
|
||||||
|
testID="notificationRule-card"
|
||||||
|
name={() => (
|
||||||
|
<ResourceList.EditableName
|
||||||
|
onUpdate={onUpdateName}
|
||||||
|
onClick={onClickName}
|
||||||
|
name={notificationRule.name}
|
||||||
|
noNameString={DEFAULT_NOTIFICATION_RULE_NAME}
|
||||||
|
parentTestID="notificationRule-card--name"
|
||||||
|
buttonTestID="notificationRule-card--name-button"
|
||||||
|
inputTestID="notificationRule-card--input"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
toggle={() => (
|
||||||
|
<SlideToggle
|
||||||
|
active={
|
||||||
|
notificationRule.status == NotificationRuleBase.StatusEnum.Active
|
||||||
|
}
|
||||||
|
size={ComponentSize.ExtraSmall}
|
||||||
|
onChange={onToggle}
|
||||||
|
testID="notificationRule-card--slide-toggle"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
// description
|
||||||
|
// labels
|
||||||
|
disabled={
|
||||||
|
notificationRule.status == NotificationRuleBase.StatusEnum.Inactive
|
||||||
|
}
|
||||||
|
contextMenu={() => (
|
||||||
|
<NotificationRuleCardContext
|
||||||
|
onDelete={onDelete}
|
||||||
|
onExport={onExport}
|
||||||
|
onClone={onClone}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
updatedAt={notificationRule.updatedAt.toString()}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const mdtp: DispatchProps = {
|
||||||
|
updateNotificationRule: updateNotificationRule,
|
||||||
|
deleteNotificationRule: deleteNotificationRule,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect<{}, DispatchProps, {}>(
|
||||||
|
null,
|
||||||
|
mdtp
|
||||||
|
)(withRouter(NotificationRuleCard))
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Libraries
|
||||||
|
import React, {FunctionComponent} from 'react'
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import {Context, IconFont} from 'src/clockface'
|
||||||
|
import {ComponentColor} from '@influxdata/clockface'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
onDelete: () => void
|
||||||
|
onClone: () => void
|
||||||
|
onExport: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const NotificationRuleCardContext: FunctionComponent<Props> = ({
|
||||||
|
onDelete,
|
||||||
|
onClone,
|
||||||
|
onExport,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Context>
|
||||||
|
<Context.Menu icon={IconFont.CogThick}>
|
||||||
|
<Context.Item label="Export" action={onExport} />
|
||||||
|
</Context.Menu>
|
||||||
|
<Context.Menu icon={IconFont.Duplicate} color={ComponentColor.Secondary}>
|
||||||
|
<Context.Item label="Clone" action={onClone} />
|
||||||
|
</Context.Menu>
|
||||||
|
<Context.Menu
|
||||||
|
icon={IconFont.Trash}
|
||||||
|
color={ComponentColor.Danger}
|
||||||
|
testID="context-delete-menu"
|
||||||
|
>
|
||||||
|
<Context.Item
|
||||||
|
label="Delete"
|
||||||
|
action={onDelete}
|
||||||
|
testID="context-delete-task"
|
||||||
|
/>
|
||||||
|
</Context.Menu>
|
||||||
|
</Context>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NotificationRuleCardContext
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Libraries
|
||||||
|
import React, {FunctionComponent} from 'react'
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import NotificationRuleCard from 'src/alerting/components/NotificationRuleCard'
|
||||||
|
import {ResourceList} from 'src/clockface'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import {NotificationRule} from 'src/types'
|
||||||
|
import {EmptyState, ComponentSize} from '@influxdata/clockface'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
notificationRules: NotificationRule[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const NotificationRuleCards: FunctionComponent<Props> = ({
|
||||||
|
notificationRules,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ResourceList>
|
||||||
|
<ResourceList.Body emptyState={<EmptyNotificationRulesList />}>
|
||||||
|
{notificationRules.map(nr => (
|
||||||
|
<NotificationRuleCard key={nr.id} notificationRule={nr} />
|
||||||
|
))}
|
||||||
|
</ResourceList.Body>
|
||||||
|
</ResourceList>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const EmptyNotificationRulesList: FunctionComponent = () => {
|
||||||
|
return (
|
||||||
|
<EmptyState size={ComponentSize.ExtraSmall}>
|
||||||
|
<EmptyState.Text
|
||||||
|
text="Looks like you don’t have any Notification Rules, why not create one?"
|
||||||
|
highlightWords={['Notification Rules']}
|
||||||
|
/>
|
||||||
|
</EmptyState>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NotificationRuleCards
|
|
@ -4,6 +4,7 @@ import {connect} from 'react-redux'
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import {NotificationRule, AppState} from 'src/types'
|
import {NotificationRule, AppState} from 'src/types'
|
||||||
|
import NotificationRuleCards from 'src/alerting/components/NotificationRuleCards'
|
||||||
|
|
||||||
interface StateProps {
|
interface StateProps {
|
||||||
notificationRules: NotificationRule[]
|
notificationRules: NotificationRule[]
|
||||||
|
@ -11,8 +12,15 @@ interface StateProps {
|
||||||
|
|
||||||
type Props = StateProps
|
type Props = StateProps
|
||||||
|
|
||||||
const NotificationRulesColumn: FunctionComponent<Props> = () => {
|
const NotificationRulesColumn: FunctionComponent<Props> = ({
|
||||||
return <>NotificationRules</>
|
notificationRules,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
NotificationRules
|
||||||
|
<NotificationRuleCards notificationRules={notificationRules} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const mstp = (state: AppState) => {
|
const mstp = (state: AppState) => {
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
import {
|
||||||
|
Check,
|
||||||
|
CheckType,
|
||||||
|
DashboardQuery,
|
||||||
|
QueryEditMode,
|
||||||
|
CheckBase,
|
||||||
|
NotificationRule,
|
||||||
|
NotificationRuleBase,
|
||||||
|
NotificationRuleType,
|
||||||
|
CheckStatusLevel,
|
||||||
|
ThresholdType,
|
||||||
|
} from 'src/types'
|
||||||
|
|
||||||
|
export const DEFAULT_CHECK_NAME = 'Name this check'
|
||||||
|
export const DEFAULT_NOTIFICATION_RULE_NAME = 'Name this notification rule'
|
||||||
|
|
||||||
|
export const query: DashboardQuery = {
|
||||||
|
text: 'this is query',
|
||||||
|
editMode: QueryEditMode.Advanced,
|
||||||
|
builderConfig: null,
|
||||||
|
name: 'great q',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const check1: Check = {
|
||||||
|
id: '1',
|
||||||
|
type: CheckType.Threshold,
|
||||||
|
name: 'Amoozing check',
|
||||||
|
orgID: 'lala',
|
||||||
|
createdAt: new Date('December 17, 2019'),
|
||||||
|
updatedAt: new Date('April 17, 2019'),
|
||||||
|
query: query,
|
||||||
|
status: CheckBase.StatusEnum.Active,
|
||||||
|
every: '2d',
|
||||||
|
offset: '1m',
|
||||||
|
tags: [{key: 'a', value: 'b'}],
|
||||||
|
statusMessageTemplate: 'this is a great message template',
|
||||||
|
thresholds: [
|
||||||
|
{
|
||||||
|
level: CheckStatusLevel.WARN,
|
||||||
|
allValues: false,
|
||||||
|
type: ThresholdType.Greater,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const check2: Check = {
|
||||||
|
id: '2',
|
||||||
|
type: CheckType.Threshold,
|
||||||
|
name: 'Another check',
|
||||||
|
orgID: 'lala',
|
||||||
|
createdAt: new Date('December 17, 2019'),
|
||||||
|
updatedAt: new Date('April 17, 2019'),
|
||||||
|
query: query,
|
||||||
|
status: CheckBase.StatusEnum.Active,
|
||||||
|
every: '2d',
|
||||||
|
offset: '1m',
|
||||||
|
tags: [{key: 'a', value: 'b'}],
|
||||||
|
statusMessageTemplate: 'this is a great message template',
|
||||||
|
thresholds: [
|
||||||
|
{
|
||||||
|
level: CheckStatusLevel.WARN,
|
||||||
|
allValues: false,
|
||||||
|
type: ThresholdType.Greater,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const checks: Array<Check> = [check1, check2]
|
||||||
|
|
||||||
|
export const notificationRule: NotificationRule = {
|
||||||
|
id: '3',
|
||||||
|
notifyEndpointID: '2',
|
||||||
|
orgID: 'lala',
|
||||||
|
createdAt: new Date('December 17, 2019'),
|
||||||
|
updatedAt: new Date('April 17, 2019'),
|
||||||
|
status: NotificationRuleBase.StatusEnum.Active,
|
||||||
|
name: 'amazing notification rule',
|
||||||
|
type: NotificationRuleType.Slack,
|
||||||
|
every: '2d',
|
||||||
|
offset: '5m',
|
||||||
|
limitEvery: 1,
|
||||||
|
limit: 5,
|
||||||
|
tagRules: [],
|
||||||
|
statusRules: [],
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
import checksReducer, {defaultChecksState} from 'src/alerting/reducers/checks'
|
||||||
|
import {
|
||||||
|
setAllChecks,
|
||||||
|
setCheck,
|
||||||
|
setCurrentCheck,
|
||||||
|
removeCheck,
|
||||||
|
} from 'src/alerting/actions/checks'
|
||||||
|
import {RemoteDataState} from 'src/types'
|
||||||
|
import {check1, check2} from 'src/alerting/constants'
|
||||||
|
|
||||||
|
describe('checksReducer', () => {
|
||||||
|
describe('setAllChecks', () => {
|
||||||
|
it('sets list and status properties of state.', () => {
|
||||||
|
const initialState = defaultChecksState
|
||||||
|
|
||||||
|
const actual = checksReducer(
|
||||||
|
initialState,
|
||||||
|
setAllChecks(RemoteDataState.Done, [check1, check2])
|
||||||
|
)
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
...defaultChecksState,
|
||||||
|
list: [check1, check2],
|
||||||
|
status: RemoteDataState.Done,
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(actual).toEqual(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('setCheck', () => {
|
||||||
|
it('adds check to list if it is new', () => {
|
||||||
|
const initialState = defaultChecksState
|
||||||
|
|
||||||
|
const actual = checksReducer(initialState, setCheck(check2))
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
...defaultChecksState,
|
||||||
|
list: [check2],
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(actual).toEqual(expected)
|
||||||
|
})
|
||||||
|
it('updates check in list if it exists', () => {
|
||||||
|
let initialState = defaultChecksState
|
||||||
|
initialState.list = [check1]
|
||||||
|
const actual = checksReducer(
|
||||||
|
initialState,
|
||||||
|
setCheck({...check1, name: check2.name})
|
||||||
|
)
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
...defaultChecksState,
|
||||||
|
list: [{...check1, name: check2.name}],
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(actual).toEqual(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('removeCheck', () => {
|
||||||
|
it('removes check from list', () => {
|
||||||
|
const initialState = defaultChecksState
|
||||||
|
initialState.list = [check1]
|
||||||
|
const actual = checksReducer(initialState, removeCheck(check1.id))
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
...defaultChecksState,
|
||||||
|
list: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(actual).toEqual(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('setCurrentCheck', () => {
|
||||||
|
it('sets current check and status.', () => {
|
||||||
|
const initialState = defaultChecksState
|
||||||
|
|
||||||
|
const actual = checksReducer(
|
||||||
|
initialState,
|
||||||
|
setCurrentCheck(RemoteDataState.Done, check1)
|
||||||
|
)
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
...defaultChecksState,
|
||||||
|
current: {status: RemoteDataState.Done, check: check1},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(actual).toEqual(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Libraries
|
||||||
|
import {produce} from 'immer'
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import {RemoteDataState, Check} from 'src/types'
|
import {RemoteDataState, Check} from 'src/types'
|
||||||
import {Action} from 'src/alerting/actions/checks'
|
import {Action} from 'src/alerting/actions/checks'
|
||||||
|
@ -17,31 +20,38 @@ export const defaultChecksState: ChecksState = {
|
||||||
export default (
|
export default (
|
||||||
state: ChecksState = defaultChecksState,
|
state: ChecksState = defaultChecksState,
|
||||||
action: Action
|
action: Action
|
||||||
): ChecksState => {
|
): ChecksState =>
|
||||||
switch (action.type) {
|
produce(state, draftState => {
|
||||||
case 'SET_CHECKS_STATUS':
|
switch (action.type) {
|
||||||
return {
|
case 'SET_ALL_CHECKS':
|
||||||
...state,
|
const {status, checks} = action.payload
|
||||||
status: action.payload.status,
|
draftState.status = status
|
||||||
}
|
if (checks) {
|
||||||
case 'SET_ALL_CHECKS':
|
draftState.list = checks
|
||||||
return {
|
}
|
||||||
...state,
|
return
|
||||||
list: action.payload.checks,
|
|
||||||
status: RemoteDataState.Done,
|
|
||||||
}
|
|
||||||
case 'SET_CHECK_STATUS':
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
current: {...state.current, status: action.payload.status},
|
|
||||||
}
|
|
||||||
case 'SET_CHECK':
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
current: {status: action.payload.status, check: action.payload.check},
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
case 'SET_CHECK':
|
||||||
return state
|
const newCheck = action.payload.check
|
||||||
}
|
const checkIndex = state.list.findIndex(c => c.id == newCheck.id)
|
||||||
}
|
|
||||||
|
if (checkIndex == -1) {
|
||||||
|
draftState.list.push(newCheck)
|
||||||
|
} else {
|
||||||
|
draftState.list[checkIndex] = newCheck
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case 'REMOVE_CHECK':
|
||||||
|
const {checkID} = action.payload
|
||||||
|
draftState.list = draftState.list.filter(c => c.id != checkID)
|
||||||
|
return
|
||||||
|
|
||||||
|
case 'SET_CURRENT_CHECK':
|
||||||
|
draftState.current.status = action.payload.status
|
||||||
|
if (action.payload.check) {
|
||||||
|
draftState.current.check = action.payload.check
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Libraries
|
||||||
|
import {produce} from 'immer'
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import {RemoteDataState, NotificationRule} from 'src/types'
|
import {RemoteDataState, NotificationRule} from 'src/types'
|
||||||
import {Action} from 'src/alerting/actions/notificationRules'
|
import {Action} from 'src/alerting/actions/notificationRules'
|
||||||
|
@ -8,43 +11,51 @@ export interface NotificationRulesState {
|
||||||
current: {status: RemoteDataState; notificationRule: NotificationRule}
|
current: {status: RemoteDataState; notificationRule: NotificationRule}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultNotificationRuleState: NotificationRulesState = {
|
export const defaultNotificationRulesState: NotificationRulesState = {
|
||||||
status: RemoteDataState.NotStarted,
|
status: RemoteDataState.NotStarted,
|
||||||
list: [],
|
list: [],
|
||||||
current: {status: RemoteDataState.NotStarted, notificationRule: null},
|
current: {status: RemoteDataState.NotStarted, notificationRule: null},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (
|
export default (
|
||||||
state: NotificationRulesState = defaultNotificationRuleState,
|
state: NotificationRulesState = defaultNotificationRulesState,
|
||||||
action: Action
|
action: Action
|
||||||
): NotificationRulesState => {
|
): NotificationRulesState =>
|
||||||
switch (action.type) {
|
produce(state, draftState => {
|
||||||
case 'SET_NOTIFICATIONRULES_STATUS':
|
switch (action.type) {
|
||||||
return {
|
case 'SET_ALL_NOTIFICATION_RULES':
|
||||||
...state,
|
const {status, notificationRules} = action.payload
|
||||||
status: action.payload.status,
|
draftState.status = status
|
||||||
}
|
if (notificationRules) {
|
||||||
case 'SET_ALL_NOTIFICATIONRULES':
|
draftState.list = notificationRules
|
||||||
return {
|
}
|
||||||
...state,
|
return
|
||||||
list: action.payload.notificationRules,
|
|
||||||
status: RemoteDataState.Done,
|
|
||||||
}
|
|
||||||
case 'SET_NOTIFICATIONRULE_STATUS':
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
current: {...state.current, status: action.payload.status},
|
|
||||||
}
|
|
||||||
case 'SET_NOTIFICATIONRULE':
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
current: {
|
|
||||||
status: action.payload.status,
|
|
||||||
notificationRule: action.payload.notificationRule,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
case 'SET_NOTIFICATION_RULE':
|
||||||
return state
|
const newNotificationRule = action.payload.notificationRule
|
||||||
}
|
const notificationRuleIndex = state.list.findIndex(
|
||||||
}
|
nr => nr.id == newNotificationRule.id
|
||||||
|
)
|
||||||
|
|
||||||
|
if (notificationRuleIndex == -1) {
|
||||||
|
draftState.list.push(newNotificationRule)
|
||||||
|
} else {
|
||||||
|
draftState.list[notificationRuleIndex] = newNotificationRule
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case 'REMOVE_NOTIFICATION_RULE':
|
||||||
|
const {notificationRuleID} = action.payload
|
||||||
|
draftState.list = draftState.list.filter(
|
||||||
|
nr => nr.id != notificationRuleID
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
case 'SET_CURRENT_NOTIFICATION_RULE':
|
||||||
|
draftState.current.status = action.payload.status
|
||||||
|
if (action.payload.notificationRule) {
|
||||||
|
draftState.current.notificationRule = action.payload.notificationRule
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
import notificationRulesReducer, {
|
||||||
|
defaultNotificationRulesState,
|
||||||
|
} from 'src/alerting/reducers/notificationRules'
|
||||||
|
import {
|
||||||
|
setAllNotificationRules,
|
||||||
|
setNotificationRule,
|
||||||
|
setCurrentNotificationRule,
|
||||||
|
removeNotificationRule,
|
||||||
|
} from 'src/alerting/actions/notificationRules'
|
||||||
|
import {RemoteDataState} from 'src/types'
|
||||||
|
import {notificationRule} from 'src/alerting/constants'
|
||||||
|
|
||||||
|
describe('notificationRulesReducer', () => {
|
||||||
|
describe('setAllNotificationRules', () => {
|
||||||
|
it('sets list and status properties of state.', () => {
|
||||||
|
const initialState = defaultNotificationRulesState
|
||||||
|
|
||||||
|
const actual = notificationRulesReducer(
|
||||||
|
initialState,
|
||||||
|
setAllNotificationRules(RemoteDataState.Done, [notificationRule])
|
||||||
|
)
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
...defaultNotificationRulesState,
|
||||||
|
list: [notificationRule],
|
||||||
|
status: RemoteDataState.Done,
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(actual).toEqual(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('setNotificationRule', () => {
|
||||||
|
it('adds notificationRule to list if it is new', () => {
|
||||||
|
const initialState = defaultNotificationRulesState
|
||||||
|
|
||||||
|
const actual = notificationRulesReducer(
|
||||||
|
initialState,
|
||||||
|
setNotificationRule(notificationRule)
|
||||||
|
)
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
...defaultNotificationRulesState,
|
||||||
|
list: [notificationRule],
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(actual).toEqual(expected)
|
||||||
|
})
|
||||||
|
it('updates notificationRule in list if it exists', () => {
|
||||||
|
let initialState = defaultNotificationRulesState
|
||||||
|
initialState.list = [notificationRule]
|
||||||
|
|
||||||
|
const actual = notificationRulesReducer(
|
||||||
|
initialState,
|
||||||
|
setNotificationRule({
|
||||||
|
...notificationRule,
|
||||||
|
name: 'moo',
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
...defaultNotificationRulesState,
|
||||||
|
list: [{...notificationRule, name: 'moo'}],
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(actual).toEqual(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('removeNotificationRule', () => {
|
||||||
|
it('removes notificationRule from list', () => {
|
||||||
|
const initialState = defaultNotificationRulesState
|
||||||
|
initialState.list = [notificationRule]
|
||||||
|
const actual = notificationRulesReducer(
|
||||||
|
initialState,
|
||||||
|
removeNotificationRule(notificationRule.id)
|
||||||
|
)
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
...defaultNotificationRulesState,
|
||||||
|
list: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(actual).toEqual(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('setCurrentNotificationRule', () => {
|
||||||
|
it('sets current notificationRule and status.', () => {
|
||||||
|
const initialState = defaultNotificationRulesState
|
||||||
|
|
||||||
|
const actual = notificationRulesReducer(
|
||||||
|
initialState,
|
||||||
|
setCurrentNotificationRule(RemoteDataState.Done, notificationRule)
|
||||||
|
)
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
...defaultNotificationRulesState,
|
||||||
|
current: {
|
||||||
|
status: RemoteDataState.Done,
|
||||||
|
notificationRule: notificationRule,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(actual).toEqual(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -29,7 +29,7 @@ interface Props {
|
||||||
onAddScraper: () => void
|
onAddScraper: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class MemberContextMenu extends PureComponent<Props> {
|
export default class BucketContextMenu extends PureComponent<Props> {
|
||||||
public render() {
|
public render() {
|
||||||
const {
|
const {
|
||||||
bucket,
|
bucket,
|
||||||
|
|
|
@ -17,16 +17,23 @@ import {VIS_THEME} from 'src/shared/constants'
|
||||||
import {INVALID_DATA_COPY} from 'src/shared/copy/cell'
|
import {INVALID_DATA_COPY} from 'src/shared/copy/cell'
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import {RemoteDataState, CheckView, TimeZone, ThresholdConfig} from 'src/types'
|
import {
|
||||||
|
RemoteDataState,
|
||||||
|
CheckView,
|
||||||
|
TimeZone,
|
||||||
|
CheckThreshold,
|
||||||
|
ThresholdType,
|
||||||
|
CheckStatusLevel,
|
||||||
|
} from 'src/types'
|
||||||
|
|
||||||
const X_COLUMN = '_time'
|
const X_COLUMN = '_time'
|
||||||
const Y_COLUMN = '_value'
|
const Y_COLUMN = '_value'
|
||||||
|
|
||||||
const THRESHOLDS: ThresholdConfig[] = [
|
const THRESHOLDS: CheckThreshold[] = [
|
||||||
{
|
{
|
||||||
type: 'less',
|
type: ThresholdType.Greater,
|
||||||
allValues: false,
|
allValues: false,
|
||||||
level: 'UNKNOWN',
|
level: CheckStatusLevel.UNKNOWN,
|
||||||
value: 20,
|
value: 20,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -46,12 +53,11 @@ const CheckPlot: FunctionComponent<Props> = ({
|
||||||
loading,
|
loading,
|
||||||
children,
|
children,
|
||||||
timeZone,
|
timeZone,
|
||||||
viewProperties: {yDomain: storedYDomain},
|
|
||||||
}) => {
|
}) => {
|
||||||
const [thresholds, setThresholds] = useState(THRESHOLDS)
|
const [thresholds, setThresholds] = useState(THRESHOLDS)
|
||||||
|
|
||||||
const [yDomain, onSetYDomain, onResetYDomain] = useVisDomainSettings(
|
const [yDomain, onSetYDomain, onResetYDomain] = useVisDomainSettings(
|
||||||
storedYDomain,
|
[0, 100],
|
||||||
table.getColumn(Y_COLUMN, 'number')
|
table.getColumn(Y_COLUMN, 'number')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -11,12 +11,12 @@ import {isInDomain, clamp} from 'src/shared/utils/vis'
|
||||||
import {DragEvent} from 'src/shared/utils/useDragBehavior'
|
import {DragEvent} from 'src/shared/utils/useDragBehavior'
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import {GreaterThresholdConfig} from 'src/types'
|
import {GreaterThreshold} from 'src/types'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
yScale: Scale<number, number>
|
yScale: Scale<number, number>
|
||||||
yDomain: number[]
|
yDomain: number[]
|
||||||
threshold: GreaterThresholdConfig
|
threshold: GreaterThreshold
|
||||||
onChangePos: (e: DragEvent) => void
|
onChangePos: (e: DragEvent) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,12 +11,12 @@ import {clamp, isInDomain} from 'src/shared/utils/vis'
|
||||||
import {DragEvent} from 'src/shared/utils/useDragBehavior'
|
import {DragEvent} from 'src/shared/utils/useDragBehavior'
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import {LessThresholdConfig} from 'src/types'
|
import {LesserThreshold} from 'src/types'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
yScale: Scale<number, number>
|
yScale: Scale<number, number>
|
||||||
yDomain: number[]
|
yDomain: number[]
|
||||||
threshold: LessThresholdConfig
|
threshold: LesserThreshold
|
||||||
onChangePos: (e: DragEvent) => void
|
onChangePos: (e: DragEvent) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,12 +11,12 @@ import {isInDomain, clamp} from 'src/shared/utils/vis'
|
||||||
import {DragEvent} from 'src/shared/utils/useDragBehavior'
|
import {DragEvent} from 'src/shared/utils/useDragBehavior'
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import {RangeThresholdConfig} from 'src/types'
|
import {RangeThreshold} from 'src/types'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
yScale: Scale<number, number>
|
yScale: Scale<number, number>
|
||||||
yDomain: number[]
|
yDomain: number[]
|
||||||
threshold: RangeThresholdConfig
|
threshold: RangeThreshold
|
||||||
onChangeMaxPos: (e: DragEvent) => void
|
onChangeMaxPos: (e: DragEvent) => void
|
||||||
onChangeMinPos: (e: DragEvent) => void
|
onChangeMinPos: (e: DragEvent) => void
|
||||||
}
|
}
|
||||||
|
@ -24,33 +24,33 @@ interface Props {
|
||||||
const RangeThresholdMarkers: FunctionComponent<Props> = ({
|
const RangeThresholdMarkers: FunctionComponent<Props> = ({
|
||||||
yScale,
|
yScale,
|
||||||
yDomain,
|
yDomain,
|
||||||
threshold: {level, within, minValue, maxValue},
|
threshold: {level, within, min, max},
|
||||||
onChangeMinPos,
|
onChangeMinPos,
|
||||||
onChangeMaxPos,
|
onChangeMaxPos,
|
||||||
}) => {
|
}) => {
|
||||||
const minY = yScale(clamp(minValue, yDomain))
|
const minY = yScale(clamp(min, yDomain))
|
||||||
const maxY = yScale(clamp(maxValue, yDomain))
|
const maxY = yScale(clamp(max, yDomain))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isInDomain(minValue, yDomain) && (
|
{isInDomain(min, yDomain) && (
|
||||||
<ThresholdMarker level={level} y={minY} onDrag={onChangeMinPos} />
|
<ThresholdMarker level={level} y={minY} onDrag={onChangeMinPos} />
|
||||||
)}
|
)}
|
||||||
{isInDomain(maxValue, yDomain) && (
|
{isInDomain(max, yDomain) && (
|
||||||
<ThresholdMarker level={level} y={maxY} onDrag={onChangeMaxPos} />
|
<ThresholdMarker level={level} y={maxY} onDrag={onChangeMaxPos} />
|
||||||
)}
|
)}
|
||||||
{within ? (
|
{within ? (
|
||||||
<ThresholdMarkerArea level={level} top={maxY} height={minY - maxY} />
|
<ThresholdMarkerArea level={level} top={maxY} height={minY - maxY} />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{maxValue <= yDomain[1] && (
|
{max <= yDomain[1] && (
|
||||||
<ThresholdMarkerArea
|
<ThresholdMarkerArea
|
||||||
level={level}
|
level={level}
|
||||||
top={yScale(yDomain[1])}
|
top={yScale(yDomain[1])}
|
||||||
height={maxY - yScale(yDomain[1])}
|
height={maxY - yScale(yDomain[1])}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{minValue >= yDomain[0] && (
|
{min >= yDomain[0] && (
|
||||||
<ThresholdMarkerArea
|
<ThresholdMarkerArea
|
||||||
level={level}
|
level={level}
|
||||||
top={minY}
|
top={minY}
|
||||||
|
|
|
@ -24,12 +24,13 @@ import {
|
||||||
VariableAssignment,
|
VariableAssignment,
|
||||||
QueryViewProperties,
|
QueryViewProperties,
|
||||||
ViewType,
|
ViewType,
|
||||||
|
CheckViewProperties,
|
||||||
} from 'src/types'
|
} from 'src/types'
|
||||||
|
|
||||||
interface OwnProps {
|
interface OwnProps {
|
||||||
timeRange: TimeRange
|
timeRange: TimeRange
|
||||||
manualRefresh: number
|
manualRefresh: number
|
||||||
properties: QueryViewProperties
|
properties: QueryViewProperties | CheckViewProperties
|
||||||
dashboardID: string
|
dashboardID: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,11 +11,11 @@ import GreaterThresholdMarker from 'src/shared/components/GreaterThresholdMarker
|
||||||
import {clamp} from 'src/shared/utils/vis'
|
import {clamp} from 'src/shared/utils/vis'
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import {ThresholdConfig} from 'src/types'
|
import {CheckThreshold, ThresholdType} from 'src/types'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
thresholds: ThresholdConfig[]
|
thresholds: CheckThreshold[]
|
||||||
onSetThresholds: (newThresholds: ThresholdConfig[]) => void
|
onSetThresholds: (newThresholds: CheckThreshold[]) => void
|
||||||
yScale: Scale<number, number>
|
yScale: Scale<number, number>
|
||||||
yDomain: number[]
|
yDomain: number[]
|
||||||
}
|
}
|
||||||
|
@ -31,18 +31,21 @@ const ThresholdMarkers: FunctionComponent<Props> = ({
|
||||||
const handleDrag = (index: number, field: string, y: number) => {
|
const handleDrag = (index: number, field: string, y: number) => {
|
||||||
const yRelative = y - originRef.current.getBoundingClientRect().top
|
const yRelative = y - originRef.current.getBoundingClientRect().top
|
||||||
const yValue = clamp(yScale.invert(yRelative), yDomain)
|
const yValue = clamp(yScale.invert(yRelative), yDomain)
|
||||||
const nextThreshold = {...thresholds[index], [field]: yValue}
|
const nextThreshold: CheckThreshold = {
|
||||||
|
...thresholds[index],
|
||||||
|
[field]: yValue,
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
nextThreshold.type === 'range' &&
|
nextThreshold.type === ThresholdType.Range &&
|
||||||
nextThreshold.minValue > nextThreshold.maxValue
|
nextThreshold.min > nextThreshold.max
|
||||||
) {
|
) {
|
||||||
// If the user drags the min past the max or vice versa, we swap the
|
// If the user drags the min past the max or vice versa, we swap the
|
||||||
// values that are set so that the min is always at most the max
|
// values that are set so that the min is always at most the max
|
||||||
const maxValue = nextThreshold.minValue
|
const maxValue = nextThreshold.min
|
||||||
|
|
||||||
nextThreshold.minValue = nextThreshold.maxValue
|
nextThreshold.min = nextThreshold.max
|
||||||
nextThreshold.maxValue = maxValue
|
nextThreshold.max = maxValue
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextThresholds = thresholds.map((t, i) =>
|
const nextThresholds = thresholds.map((t, i) =>
|
||||||
|
@ -60,7 +63,7 @@ const ThresholdMarkers: FunctionComponent<Props> = ({
|
||||||
const onChangeMinPos = ({y}) => handleDrag(index, 'minValue', y)
|
const onChangeMinPos = ({y}) => handleDrag(index, 'minValue', y)
|
||||||
|
|
||||||
switch (threshold.type) {
|
switch (threshold.type) {
|
||||||
case 'greater':
|
case ThresholdType.Greater:
|
||||||
return (
|
return (
|
||||||
<GreaterThresholdMarker
|
<GreaterThresholdMarker
|
||||||
key={index}
|
key={index}
|
||||||
|
@ -70,7 +73,7 @@ const ThresholdMarkers: FunctionComponent<Props> = ({
|
||||||
onChangePos={onChangePos}
|
onChangePos={onChangePos}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
case 'less':
|
case ThresholdType.Lesser:
|
||||||
return (
|
return (
|
||||||
<LessThresholdMarker
|
<LessThresholdMarker
|
||||||
key={index}
|
key={index}
|
||||||
|
@ -80,7 +83,7 @@ const ThresholdMarkers: FunctionComponent<Props> = ({
|
||||||
onChangePos={onChangePos}
|
onChangePos={onChangePos}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
case 'range':
|
case ThresholdType.Range:
|
||||||
return (
|
return (
|
||||||
<RangeThresholdMarkers
|
<RangeThresholdMarkers
|
||||||
key={index}
|
key={index}
|
||||||
|
|
|
@ -22,13 +22,14 @@ import {
|
||||||
XYViewGeom,
|
XYViewGeom,
|
||||||
RemoteDataState,
|
RemoteDataState,
|
||||||
TimeZone,
|
TimeZone,
|
||||||
|
CheckViewProperties,
|
||||||
} from 'src/types'
|
} from 'src/types'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
giraffeResult: FromFluxResult
|
giraffeResult: FromFluxResult
|
||||||
files: string[]
|
files: string[]
|
||||||
loading: RemoteDataState
|
loading: RemoteDataState
|
||||||
properties: QueryViewProperties
|
properties: QueryViewProperties | CheckViewProperties
|
||||||
timeZone: TimeZone
|
timeZone: TimeZone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -747,3 +747,39 @@ export const getNotificationRuleFailed = (message: string): Notification => ({
|
||||||
...defaultErrorNotification,
|
...defaultErrorNotification,
|
||||||
message: `Failed to get notification rule: ${message}`,
|
message: `Failed to get notification rule: ${message}`,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const createCheckFailed = (message: string): Notification => ({
|
||||||
|
...defaultErrorNotification,
|
||||||
|
message: `Failed to create check: ${message}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const updateCheckFailed = (message: string): Notification => ({
|
||||||
|
...defaultErrorNotification,
|
||||||
|
message: `Failed to update check: ${message}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const deleteCheckFailed = (message: string): Notification => ({
|
||||||
|
...defaultErrorNotification,
|
||||||
|
message: `Failed to delete check: ${message}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const createNotificationRuleFailed = (
|
||||||
|
message: string
|
||||||
|
): Notification => ({
|
||||||
|
...defaultErrorNotification,
|
||||||
|
message: `Failed to create notification rule: ${message}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const updateNotificationRuleFailed = (
|
||||||
|
message: string
|
||||||
|
): Notification => ({
|
||||||
|
...defaultErrorNotification,
|
||||||
|
message: `Failed to update notification rule: ${message}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const deleteNotificationRuleFailed = (
|
||||||
|
message: string
|
||||||
|
): Notification => ({
|
||||||
|
...defaultErrorNotification,
|
||||||
|
message: `Failed to delete notification rule: ${message}`,
|
||||||
|
})
|
||||||
|
|
|
@ -28,7 +28,7 @@ import {
|
||||||
QueryView,
|
QueryView,
|
||||||
QueryViewProperties,
|
QueryViewProperties,
|
||||||
ExtractWorkingView,
|
ExtractWorkingView,
|
||||||
AggregateWindow,
|
BuilderConfigAggregateWindow,
|
||||||
} from 'src/types/dashboards'
|
} from 'src/types/dashboards'
|
||||||
import {Action} from 'src/timeMachine/actions'
|
import {Action} from 'src/timeMachine/actions'
|
||||||
import {TimeMachineTab} from 'src/types/timeMachine'
|
import {TimeMachineTab} from 'src/types/timeMachine'
|
||||||
|
@ -39,7 +39,7 @@ interface QueryBuilderState {
|
||||||
buckets: string[]
|
buckets: string[]
|
||||||
bucketsStatus: RemoteDataState
|
bucketsStatus: RemoteDataState
|
||||||
functions: Array<[{name: string}]>
|
functions: Array<[{name: string}]>
|
||||||
aggregateWindow: AggregateWindow
|
aggregateWindow: BuilderConfigAggregateWindow
|
||||||
tags: Array<{
|
tags: Array<{
|
||||||
valuesSearchTerm: string
|
valuesSearchTerm: string
|
||||||
keysSearchTerm: string
|
keysSearchTerm: string
|
||||||
|
|
|
@ -1 +1,40 @@
|
||||||
export {Check, NotificationRule} from '@influxdata/influx'
|
export {
|
||||||
|
Check,
|
||||||
|
CheckType,
|
||||||
|
CheckBaseTags,
|
||||||
|
CheckBase,
|
||||||
|
CheckStatusLevel,
|
||||||
|
ThresholdCheck,
|
||||||
|
ThresholdType,
|
||||||
|
GreaterThreshold,
|
||||||
|
LesserThreshold,
|
||||||
|
RangeThreshold,
|
||||||
|
DeadmanCheck,
|
||||||
|
NotificationRuleType,
|
||||||
|
CheckThreshold,
|
||||||
|
NotificationRuleBase,
|
||||||
|
} from '@influxdata/influx'
|
||||||
|
|
||||||
|
import {
|
||||||
|
SlackNotificationRule as SlackNotificationRuleAPI,
|
||||||
|
SMTPNotificationRule as SMTPNotificationRuleAPI,
|
||||||
|
PagerDutyNotificationRule as PagerDutyNotificationRuleAPI,
|
||||||
|
} from '@influxdata/influx'
|
||||||
|
|
||||||
|
export interface SlackNotificationRule extends SlackNotificationRuleAPI {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SMTPNotificationRule extends SMTPNotificationRuleAPI {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PagerDutyNotificationRule
|
||||||
|
extends PagerDutyNotificationRuleAPI {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NotificationRule =
|
||||||
|
| SlackNotificationRule
|
||||||
|
| SMTPNotificationRule
|
||||||
|
| PagerDutyNotificationRule
|
||||||
|
|
|
@ -5,6 +5,16 @@ import {
|
||||||
IDashboard as DashboardAPI,
|
IDashboard as DashboardAPI,
|
||||||
Cell as CellAPI,
|
Cell as CellAPI,
|
||||||
ViewLinks,
|
ViewLinks,
|
||||||
|
DashboardQuery,
|
||||||
|
CheckViewProperties,
|
||||||
|
} from '@influxdata/influx'
|
||||||
|
|
||||||
|
export {
|
||||||
|
CheckView,
|
||||||
|
CheckViewProperties,
|
||||||
|
DashboardQuery,
|
||||||
|
BuilderConfig,
|
||||||
|
BuilderConfigAggregateWindow,
|
||||||
} from '@influxdata/influx'
|
} from '@influxdata/influx'
|
||||||
|
|
||||||
export enum Scale {
|
export enum Scale {
|
||||||
|
@ -57,24 +67,6 @@ export enum QueryEditMode {
|
||||||
Advanced = 'advanced',
|
Advanced = 'advanced',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AggregateWindow {
|
|
||||||
period: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BuilderConfig {
|
|
||||||
buckets: string[]
|
|
||||||
tags: Array<{key: string; values: string[]}>
|
|
||||||
functions: Array<{name: string}>
|
|
||||||
aggregateWindow: AggregateWindow
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DashboardQuery {
|
|
||||||
text: string
|
|
||||||
editMode: QueryEditMode
|
|
||||||
builderConfig: BuilderConfig
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DashboardDraftQuery extends DashboardQuery {
|
export interface DashboardDraftQuery extends DashboardQuery {
|
||||||
hidden: boolean
|
hidden: boolean
|
||||||
}
|
}
|
||||||
|
@ -129,7 +121,7 @@ export type ViewProperties =
|
||||||
| HistogramView
|
| HistogramView
|
||||||
| HeatmapView
|
| HeatmapView
|
||||||
| ScatterView
|
| ScatterView
|
||||||
| CheckView
|
| CheckViewProperties
|
||||||
|
|
||||||
export type QueryViewProperties = Extract<
|
export type QueryViewProperties = Extract<
|
||||||
ViewProperties,
|
ViewProperties,
|
||||||
|
@ -290,47 +282,6 @@ export interface ScatterView {
|
||||||
showNoteWhenEmpty: boolean
|
showNoteWhenEmpty: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CheckStatusLevel = 'OK' | 'INFO' | 'WARN' | 'CRIT' | 'UNKNOWN'
|
|
||||||
|
|
||||||
export interface GreaterThresholdConfig {
|
|
||||||
type: 'greater'
|
|
||||||
level: CheckStatusLevel
|
|
||||||
allValues: boolean
|
|
||||||
value: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LessThresholdConfig {
|
|
||||||
type: 'less'
|
|
||||||
level: CheckStatusLevel
|
|
||||||
allValues: boolean
|
|
||||||
value: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RangeThresholdConfig {
|
|
||||||
type: 'range'
|
|
||||||
level: CheckStatusLevel
|
|
||||||
allValues: boolean
|
|
||||||
minValue: number
|
|
||||||
maxValue: number
|
|
||||||
within: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ThresholdConfig =
|
|
||||||
| GreaterThresholdConfig
|
|
||||||
| LessThresholdConfig
|
|
||||||
| RangeThresholdConfig
|
|
||||||
|
|
||||||
export interface CheckView {
|
|
||||||
type: ViewType.Check
|
|
||||||
shape: ViewShape.ChronografV2
|
|
||||||
queries: DashboardQuery[]
|
|
||||||
thresholds: ThresholdConfig[]
|
|
||||||
yDomain: [number, number]
|
|
||||||
colors: string[]
|
|
||||||
note: string
|
|
||||||
showNoteWhenEmpty: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MarkdownView {
|
export interface MarkdownView {
|
||||||
type: ViewType.Markdown
|
type: ViewType.Markdown
|
||||||
shape: ViewShape.ChronografV2
|
shape: ViewShape.ChronografV2
|
||||||
|
@ -367,12 +318,6 @@ interface DashboardFileMetaSection {
|
||||||
|
|
||||||
export type NewCell = Omit<Cell, 'id' | 'links' | 'dashboardID'>
|
export type NewCell = Omit<Cell, 'id' | 'links' | 'dashboardID'>
|
||||||
|
|
||||||
export enum ThresholdType {
|
|
||||||
Text = 'text',
|
|
||||||
BG = 'background',
|
|
||||||
Base = 'base',
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DashboardSwitcherLink {
|
export interface DashboardSwitcherLink {
|
||||||
key: string
|
key: string
|
||||||
text: string
|
text: string
|
||||||
|
|
|
@ -1151,10 +1151,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@influxdata/giraffe/-/giraffe-0.16.0.tgz#b04a304460a7c9449fe1a9fa2a3f5db97949e916"
|
resolved "https://registry.yarnpkg.com/@influxdata/giraffe/-/giraffe-0.16.0.tgz#b04a304460a7c9449fe1a9fa2a3f5db97949e916"
|
||||||
integrity sha512-nDVQgx5Lq3fjsMXTzYwzf0HXKjSxzsBJibitObwtE0wefpzU4LW0IEUrcCBNIQyXj1OxyBNL9a67gZ4DyV7M2w==
|
integrity sha512-nDVQgx5Lq3fjsMXTzYwzf0HXKjSxzsBJibitObwtE0wefpzU4LW0IEUrcCBNIQyXj1OxyBNL9a67gZ4DyV7M2w==
|
||||||
|
|
||||||
"@influxdata/influx@0.5.0":
|
"@influxdata/influx@0.5.5":
|
||||||
version "0.5.0"
|
version "0.5.5"
|
||||||
resolved "https://registry.yarnpkg.com/@influxdata/influx/-/influx-0.5.0.tgz#dedf688d84a50b526a15362633bd5ee7f1e7a95b"
|
resolved "https://registry.yarnpkg.com/@influxdata/influx/-/influx-0.5.5.tgz#ff30862ba3837df8e6e237634e7c844a00c03c08"
|
||||||
integrity sha512-P24C5j20RRMX2JT43vTdoSozjZp01rcXxOAoIMR+sDMSMG4XuuckIEEOsdTnP1w+N2ivhjC74MjzmswxKeO0fw==
|
integrity sha512-mmyymYIT/HpWDrdR1/x9+ENJIrZTFyr46EF42adqRwujwLxVyG1MPOC7H6aoH87DZgVxKV4nt+zmRSgOnetdpg==
|
||||||
dependencies:
|
dependencies:
|
||||||
axios "^0.19.0"
|
axios "^0.19.0"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue