Add Any to rule levels (#14866)

* WIP

* Fix UI linter errors from swagger changes to Level Rule

* Prevent same level selection on changes from

* Remove unused get

* Fix prettier error

* chore(notification/rule): change level rule to check level for rules
pull/14883/head
Deniz Kusefoglu 2019-08-29 17:46:00 -07:00 committed by GitHub
parent c4ceb69a5d
commit 9450149561
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 195 additions and 248 deletions

View File

@ -4,7 +4,6 @@ import (
"context"
"encoding/json"
"testing"
"time"
"github.com/influxdata/influxdb/notification"
@ -61,14 +60,10 @@ func Test_newNotificationRuleResponses(t *testing.T) {
},
StatusRules: []notification.StatusRule{
{
CurrentLevel: notification.LevelRule{CheckLevel: notification.Critical, Operation: true},
Count: 3,
Period: influxdb.Duration{Duration: time.Hour},
CurrentLevel: notification.Critical,
},
{
CurrentLevel: notification.LevelRule{CheckLevel: notification.Warn, Operation: false},
Count: 30,
Period: influxdb.Duration{Duration: time.Minute * 30},
CurrentLevel: notification.Warn,
},
},
},
@ -93,36 +88,34 @@ func Test_newNotificationRuleResponses(t *testing.T) {
},
"notificationRules": [
{
"ownerID": "0000000000000003",
"channel": "ch1",
"createdAt": "0001-01-01T00:00:00Z",
"description": "desc1",
"endpointID": "0000000000000004",
"every": "5m",
"id": "0000000000000001",
"labels": [
],
"links": {
"labels": "/api/v2/notificationRules/0000000000000001/labels",
"members": "/api/v2/notificationRules/0000000000000001/members",
"owners": "/api/v2/notificationRules/0000000000000001/owners",
"self": "/api/v2/notificationRules/0000000000000001"
},
"messageTemplate": "message 1{var1}",
"name": "name1",
"offset": "15s",
"orgID": "0000000000000002",
"ownerID": "0000000000000003",
"runbookLink": "",
"status": "active",
"statusRules": [
{
"count": 3,
"currentLevel": {
"level": "CRIT",
"operation": "equal"
},
"period": "1h0m0s",
"currentLevel": "CRIT",
"previousLevel": null
},
{
"count": 30,
"currentLevel": {
"level": "WARN",
"operation": "notequal"
},
"period": "30m0s",
"currentLevel": "WARN",
"previousLevel": null
}
],
@ -139,38 +132,33 @@ func Test_newNotificationRuleResponses(t *testing.T) {
}
],
"type": "slack",
"updatedAt": "0001-01-01T00:00:00Z",
"labels": [],
"links": {
"labels": "/api/v2/notificationRules/0000000000000001/labels",
"members": "/api/v2/notificationRules/0000000000000001/members",
"owners": "/api/v2/notificationRules/0000000000000001/owners",
"self": "/api/v2/notificationRules/0000000000000001"
}
"updatedAt": "0001-01-01T00:00:00Z"
},
{
"ownerID": "0000000000000021",
"messageTemplate": "body 2{var2}",
"createdAt": "0001-01-01T00:00:00Z",
"description": "desc2",
"endpointID": "000000000000002c",
"id": "000000000000000b",
"name": "name2",
"orgID": "0000000000000002",
"runbookLink": "",
"status": "inactive",
"type": "pagerduty",
"updatedAt": "0001-01-01T00:00:00Z",
"labels": [],
"labels": [
],
"links": {
"labels": "/api/v2/notificationRules/000000000000000b/labels",
"members": "/api/v2/notificationRules/000000000000000b/members",
"owners": "/api/v2/notificationRules/000000000000000b/owners",
"self": "/api/v2/notificationRules/000000000000000b"
}
},
"messageTemplate": "body 2{var2}",
"name": "name2",
"orgID": "0000000000000002",
"ownerID": "0000000000000021",
"runbookLink": "",
"status": "inactive",
"type": "pagerduty",
"updatedAt": "0001-01-01T00:00:00Z"
}
]
}`},
}`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@ -224,14 +212,10 @@ func Test_newNotificationRuleResponse(t *testing.T) {
},
StatusRules: []notification.StatusRule{
{
CurrentLevel: notification.LevelRule{CheckLevel: notification.Critical, Operation: true},
Count: 3,
Period: influxdb.Duration{Duration: time.Hour},
CurrentLevel: notification.Critical,
},
{
CurrentLevel: notification.LevelRule{CheckLevel: notification.Warn, Operation: true},
Count: 30,
Period: influxdb.Duration{Duration: time.Minute * 30},
CurrentLevel: notification.Warn,
},
},
},
@ -239,35 +223,33 @@ func Test_newNotificationRuleResponse(t *testing.T) {
},
want: `{
"channel": "ch1",
"messageTemplate": "message 1{var1}",
"createdAt": "0001-01-01T00:00:00Z",
"description": "desc1",
"endpointID": "0000000000000004",
"every": "5m",
"id": "0000000000000001",
"labels": [
],
"links": {
"labels": "/api/v2/notificationRules/0000000000000001/labels",
"members": "/api/v2/notificationRules/0000000000000001/members",
"owners": "/api/v2/notificationRules/0000000000000001/owners",
"self": "/api/v2/notificationRules/0000000000000001"
},
"messageTemplate": "message 1{var1}",
"name": "name1",
"offset": "15s",
"orgID": "0000000000000002",
"ownerID": "0000000000000003",
"endpointID": "0000000000000004",
"name": "name1",
"description": "desc1",
"every": "5m",
"offset": "15s",
"type": "slack",
"runbookLink": "",
"status": "active",
"statusRules": [
{
"count": 3,
"currentLevel": {
"level": "CRIT",
"operation": "equal"
},
"period": "1h0m0s",
"currentLevel": "CRIT",
"previousLevel": null
},
{
"count": 30,
"currentLevel": {
"level": "WARN",
"operation": "equal"
},
"period": "30m0s",
"currentLevel": "WARN",
"previousLevel": null
}
],
@ -283,18 +265,9 @@ func Test_newNotificationRuleResponse(t *testing.T) {
"value": "v2"
}
],
"createdAt": "0001-01-01T00:00:00Z",
"updatedAt": "0001-01-01T00:00:00Z",
"labels": [
],
"links": {
"labels": "/api/v2/notificationRules/0000000000000001/labels",
"members": "/api/v2/notificationRules/0000000000000001/members",
"owners": "/api/v2/notificationRules/0000000000000001/owners",
"self": "/api/v2/notificationRules/0000000000000001"
}
}`,
"type": "slack",
"updatedAt": "0001-01-01T00:00:00Z"
}`,
},
}
for _, tt := range tests {

View File

@ -9413,6 +9413,10 @@ components:
description: the state to record if check matches a criteria
type: string
enum: ["UNKNOWN", "OK", "INFO", "CRIT", "WARN"]
RuleStatusLevel:
description: the state to record if check matches a criteria
type: string
enum: ["UNKNOWN", "OK", "INFO", "CRIT", "WARN", "ANY"]
NotificationRuleUpdate:
type: object
properties:
@ -9524,21 +9528,13 @@ components:
type: object
properties:
currentLevel:
$ref: "#/components/schemas/LevelRule"
$ref: "#/components/schemas/RuleStatusLevel"
previousLevel:
$ref: "#/components/schemas/LevelRule"
$ref: "#/components/schemas/RuleStatusLevel"
count:
type: integer
period:
type: string
LevelRule:
type: object
properties:
level:
$ref: "#/components/schemas/CheckStatusLevel"
operation:
type: string
enum: ["equal", "notequal"]
HTTPNotificationRuleBase:
type: object
required: [type, url]

View File

@ -15,6 +15,10 @@ import (
influxTesting "github.com/influxdata/influxdb/testing"
)
func lvlPtr(l notification.CheckLevel) *notification.CheckLevel {
return &l
}
const (
id1 = "020f755c3c082000"
id2 = "020f755c3c082001"
@ -307,13 +311,11 @@ func TestJSON(t *testing.T) {
},
StatusRules: []notification.StatusRule{
{
CurrentLevel: notification.LevelRule{CheckLevel: notification.Warn, Operation: true},
PreviousLevel: &notification.LevelRule{CheckLevel: notification.Critical, Operation: false},
Count: 3,
Period: influxdb.Duration{Duration: time.Minute * 13},
CurrentLevel: notification.Warn,
PreviousLevel: lvlPtr(notification.Critical),
},
{
CurrentLevel: notification.LevelRule{CheckLevel: notification.Critical, Operation: true},
CurrentLevel: notification.Critical,
},
},
CRUDLog: influxdb.CRUDLog{

View File

@ -3,44 +3,12 @@ package notification
import (
"encoding/json"
"strings"
"github.com/influxdata/influxdb"
)
// StatusRule includes parametes of status rules.
type StatusRule struct {
CurrentLevel LevelRule `json:"currentLevel"`
PreviousLevel *LevelRule `json:"previousLevel"`
// Alert when >= Count per Period
Count int `json:"count"`
Period influxdb.Duration `json:"period"`
}
// LevelRule is a pair of level + operation.
type LevelRule struct {
CheckLevel CheckLevel `json:"level"`
Operation StatusOperation `json:"operation"`
}
// StatusOperation is either equal or notequal
type StatusOperation bool
// MarshalJSON implements json.Marshaler interface.
func (op StatusOperation) MarshalJSON() ([]byte, error) {
if op {
return json.Marshal("equal")
}
return json.Marshal("notequal")
}
// UnmarshalJSON implements json.Unmarshaler interface.
func (op *StatusOperation) UnmarshalJSON(b []byte) error {
var ss string
if err := json.Unmarshal(b, &ss); err != nil {
return err
}
*op = (ss == "equal")
return nil
CurrentLevel CheckLevel `json:"currentLevel"`
PreviousLevel *CheckLevel `json:"previousLevel"`
}
// CheckLevel is the enum value of status levels.
@ -53,6 +21,7 @@ const (
Info
Warn
Critical
Any
)
var checkLevels = []string{
@ -61,6 +30,7 @@ var checkLevels = []string{
"INFO",
"WARN",
"CRIT",
"ANY",
}
var checkLevelMaps = map[string]CheckLevel{
@ -69,6 +39,7 @@ var checkLevelMaps = map[string]CheckLevel{
"INFO": Info,
"WARN": Warn,
"CRIT": Critical,
"ANY": Any,
}
// MarshalJSON implements json.Marshaller.

View File

@ -3,12 +3,14 @@ package notification
import (
"encoding/json"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/influxdata/influxdb"
)
func checkLvlPtr(l CheckLevel) *CheckLevel {
return &l
}
func TestStatusJSON(t *testing.T) {
cases := []struct {
name string
@ -18,16 +20,12 @@ func TestStatusJSON(t *testing.T) {
{
name: "regular status rule",
src: StatusRule{
CurrentLevel: LevelRule{Operation: true, CheckLevel: Warn},
PreviousLevel: &LevelRule{Operation: false, CheckLevel: Critical},
Count: 3,
Period: influxdb.Duration{Duration: time.Minute * 13},
CurrentLevel: Warn,
PreviousLevel: checkLvlPtr(Critical),
},
target: StatusRule{
CurrentLevel: LevelRule{Operation: true, CheckLevel: Warn},
PreviousLevel: &LevelRule{Operation: false, CheckLevel: Critical},
Count: 3,
Period: influxdb.Duration{Duration: time.Minute * 13},
CurrentLevel: Warn,
PreviousLevel: checkLvlPtr(Critical),
},
},
{
@ -38,10 +36,10 @@ func TestStatusJSON(t *testing.T) {
{
name: "invalid status",
src: StatusRule{
CurrentLevel: LevelRule{CheckLevel: CheckLevel(-10)},
CurrentLevel: CheckLevel(-10),
},
target: StatusRule{
CurrentLevel: LevelRule{CheckLevel: Unknown},
CurrentLevel: Unknown,
},
},
}

View File

@ -10,27 +10,35 @@ import {
} from '@influxdata/clockface'
// Types
import {CheckStatusLevel} from 'src/types'
import {RuleStatusLevel} from 'src/types'
type Level = CheckStatusLevel
type LevelType = 'currentLevel' | 'previousLevel'
type ColorLevel = {hex: InfluxColors; display: string; value: RuleStatusLevel}
type ColorLevel = {hex: InfluxColors; display: string; value: Level}
interface Props {
selectedLevel: RuleStatusLevel
otherLevel: RuleStatusLevel
type: LevelType
onClickLevel: (type: LevelType, level: RuleStatusLevel) => void
}
const levels: ColorLevel[] = [
const RuleLevelsDropdown: FC<Props> = ({
type,
selectedLevel,
otherLevel,
onClickLevel,
}) => {
let levels: ColorLevel[] = [
{display: 'CRIT', hex: InfluxColors.Fire, value: 'CRIT'},
{display: 'INFO', hex: InfluxColors.Ocean, value: 'INFO'},
{display: 'WARN', hex: InfluxColors.Thunder, value: 'WARN'},
{display: 'OK', hex: InfluxColors.Viridian, value: 'OK'},
]
{display: 'ANY', hex: InfluxColors.Sidewalk, value: 'ANY'},
]
if (otherLevel) {
levels = levels.filter(l => l.value !== otherLevel)
}
interface Props {
selectedLevel: Level
type: LevelType
onClickLevel: (type: LevelType, level: Level) => void
}
const LevelsDropdown: FC<Props> = ({type, selectedLevel, onClickLevel}) => {
const selected = levels.find(l => l.value === selectedLevel)
if (!selected) {
@ -83,4 +91,4 @@ const LevelsDropdown: FC<Props> = ({type, selectedLevel, onClickLevel}) => {
)
}
export default LevelsDropdown
export default RuleLevelsDropdown

View File

@ -3,7 +3,7 @@ import {
NotificationRuleDraft,
StatusRuleDraft,
TagRuleDraft,
CheckStatusLevel,
RuleStatusLevel,
} from 'src/types'
export type LevelType = 'currentLevel' | 'previousLevel'
@ -14,7 +14,7 @@ export type Action =
type: 'UPDATE_STATUS_LEVEL'
statusID: string
levelType: LevelType
level: CheckStatusLevel
level: RuleStatusLevel
}
| {type: 'SET_ACTIVE_SCHEDULE'; schedule: 'cron' | 'every'}
| {type: 'UPDATE_STATUS_RULES'; statusRule: StatusRuleDraft}

View File

@ -97,10 +97,7 @@ export const reducer = (state: RuleState, action: Action) => {
const value = {
...status.value,
[levelType]: {
...status.value[levelType],
level,
},
[levelType]: level,
}
return {...status, value}

View File

@ -8,7 +8,7 @@ import {
FlexDirection,
ComponentSize,
} from '@influxdata/clockface'
import LevelsDropdown from 'src/alerting/components/notifications/LevelsDropdown'
import RuleLevelsDropdown from 'src/alerting/components/notifications/RuleLevelsDropdown'
import StatusChangeDropdown from 'src/alerting/components/notifications/StatusChangeDropdown'
import {LevelType} from 'src/alerting/components/notifications/RuleOverlay.reducer'
@ -16,7 +16,7 @@ import {LevelType} from 'src/alerting/components/notifications/RuleOverlay.reduc
import {useRuleDispatch} from './RuleOverlayProvider'
// Types
import {StatusRuleDraft, CheckStatusLevel} from 'src/types'
import {StatusRuleDraft, RuleStatusLevel} from 'src/types'
interface Props {
status: StatusRuleDraft
@ -26,7 +26,7 @@ const StatusLevels: FC<Props> = ({status}) => {
const {currentLevel, previousLevel} = status.value
const dispatch = useRuleDispatch()
const onClickLevel = (levelType: LevelType, level: CheckStatusLevel) => {
const onClickLevel = (levelType: LevelType, level: RuleStatusLevel) => {
dispatch({
type: 'UPDATE_STATUS_LEVEL',
statusID: status.cid,
@ -43,18 +43,20 @@ const StatusLevels: FC<Props> = ({status}) => {
</FlexBox.Child>
{!!previousLevel && (
<FlexBox.Child grow={0} basis={140}>
<LevelsDropdown
<RuleLevelsDropdown
type="previousLevel"
selectedLevel={previousLevel.level}
selectedLevel={previousLevel}
otherLevel={currentLevel}
onClickLevel={onClickLevel}
/>
</FlexBox.Child>
)}
{!!previousLevel && <TextBlock text="to" />}
<FlexBox.Child grow={0} basis={140}>
<LevelsDropdown
<RuleLevelsDropdown
type="currentLevel"
selectedLevel={currentLevel.level}
selectedLevel={currentLevel}
otherLevel={previousLevel}
onClickLevel={onClickLevel}
/>
</FlexBox.Child>

View File

@ -7,7 +7,6 @@ import {
StatusRule,
TagRule,
StatusRuleDraft,
LevelRule,
SlackNotificationRuleBase,
SMTPNotificationRuleBase,
PagerDutyNotificationRuleBase,
@ -16,6 +15,7 @@ import {
NotificationRuleDraft,
NewNotificationRule,
HTTPNotificationRuleBase,
RuleStatusLevel,
} from 'src/types'
type RuleVariantFields =
@ -61,13 +61,13 @@ export const activeChange = (status: StatusRuleDraft) => {
return 'is equal to'
}
export const previousLevel: LevelRule = {level: 'OK'}
export const previousLevel = 'OK' as RuleStatusLevel
export const changeStatusRule = (
status: StatusRuleDraft,
change: Change
changeType: Change
): StatusRuleDraft => {
if (change === 'is equal to') {
if (changeType === 'is equal to') {
return omit(status, 'value.previousLevel') as StatusRuleDraft
}
@ -95,7 +95,7 @@ export const initRuleDraft = (orgID: string): NotificationRuleDraft => ({
{
cid: uuid.v4(),
value: {
currentLevel: {operation: 'equal', level: 'CRIT'},
currentLevel: 'CRIT',
period: '1h',
count: 1,
},

View File

@ -73,9 +73,9 @@ export {
Threshold,
CheckBase,
StatusRule,
LevelRule,
TagRule,
CheckStatusLevel,
RuleStatusLevel,
GreaterThreshold,
LesserThreshold,
RangeThreshold,