Merge branch 'master' of github.com:influxdata/chronograf
commit
abab439eed
|
@ -5,6 +5,7 @@
|
|||
1. [#3233](https://github.com/influxdata/chronograf/pull/3233): Add default retention policy field as option in source configuration for use in querying hosts from Host List page & Host pages
|
||||
1. [#3290](https://github.com/influxdata/chronograf/pull/3290): Add support for PagerDuty v2 in UI
|
||||
1. [#3369](https://github.com/influxdata/chronograf/pull/3369): Add support for OpsGenie v2 in UI
|
||||
1. [#3386](https://github.com/influxdata/chronograf/pull/3386): Add support for Kafka in UI to configure and create alert handlers
|
||||
1. [#3416](https://github.com/influxdata/chronograf/pull/3416): Allow kapacitor services to be disabled
|
||||
|
||||
### UI Improvements
|
||||
|
|
10
kapacitor.go
10
kapacitor.go
|
@ -22,7 +22,8 @@ type AlertNodes struct {
|
|||
Alerta []*Alerta `json:"alerta"` // Alerta will send alert to all Alerta
|
||||
OpsGenie []*OpsGenie `json:"opsGenie"` // OpsGenie will send alert to all OpsGenie
|
||||
OpsGenie2 []*OpsGenie `json:"opsGenie2"` // OpsGenie2 will send alert to all OpsGenie v2
|
||||
Talk []*Talk `json:"talk"` // Talk will send alert to all Talk
|
||||
Talk []*Talk `json:"talk"` // Talk will send alert to all Talk
|
||||
Kafka []*Kafka `json:"kafka"` // Kafka will send alert to all Kafka
|
||||
}
|
||||
|
||||
// Post will POST alerts to a destination URL
|
||||
|
@ -135,6 +136,13 @@ type OpsGenie struct {
|
|||
// Talk sends alerts to Jane Talk (https://jianliao.com/site)
|
||||
type Talk struct{}
|
||||
|
||||
// Kafka sends alerts to any Kafka brokers specified in the handler config
|
||||
type Kafka struct {
|
||||
Cluster string `json:"cluster"`
|
||||
Topic string `json:"topic"`
|
||||
Template string `json:"template"`
|
||||
}
|
||||
|
||||
// MarshalJSON converts AlertNodes to JSON
|
||||
func (n *AlertNodes) MarshalJSON() ([]byte, error) {
|
||||
type Alias AlertNodes
|
||||
|
|
|
@ -410,6 +410,10 @@ func newAlertResponse(task *kapa.Task, srcID, kapaID int) *alertResponse {
|
|||
res.AlertNodes.HipChat = []*chronograf.HipChat{}
|
||||
}
|
||||
|
||||
if res.AlertNodes.Kafka == nil {
|
||||
res.AlertNodes.Kafka = []*chronograf.Kafka{}
|
||||
}
|
||||
|
||||
if res.AlertNodes.Log == nil {
|
||||
res.AlertNodes.Log = []*chronograf.Log{}
|
||||
}
|
||||
|
|
|
@ -132,6 +132,7 @@ func Test_KapacitorRulesGet(t *testing.T) {
|
|||
OpsGenie: []*chronograf.OpsGenie{},
|
||||
OpsGenie2: []*chronograf.OpsGenie{},
|
||||
Talk: []*chronograf.Talk{},
|
||||
Kafka: []*chronograf.Kafka{},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -3677,6 +3677,7 @@
|
|||
"post",
|
||||
"http",
|
||||
"hipchat",
|
||||
"kafka",
|
||||
"opsgenie",
|
||||
"opsgenie2",
|
||||
"pagerduty",
|
||||
|
|
|
@ -24,7 +24,7 @@ import {DEFAULT_HOME_PAGE} from 'src/shared/constants'
|
|||
|
||||
import * as copy from 'src/shared/copy/notifications'
|
||||
|
||||
import {Source, Me} from 'src/types'
|
||||
import {Source, Me, Notification, NotificationFunc} from 'src/types'
|
||||
|
||||
interface Auth {
|
||||
isUsingAuth: boolean
|
||||
|
@ -47,7 +47,7 @@ interface Props {
|
|||
router: InjectedRouter
|
||||
location: Location
|
||||
auth: Auth
|
||||
notify: () => void
|
||||
notify: (message: Notification | NotificationFunc) => void
|
||||
errorThrown: () => void
|
||||
}
|
||||
|
||||
|
|
|
@ -8,10 +8,17 @@ import {notify as notifyAction} from 'src/shared/actions/notifications'
|
|||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
import AllUsersTable from 'src/admin/components/chronograf/AllUsersTable'
|
||||
import {AuthLinks, Organization, Role, User} from 'src/types'
|
||||
import {
|
||||
AuthLinks,
|
||||
Organization,
|
||||
Role,
|
||||
User,
|
||||
Notification,
|
||||
NotificationFunc,
|
||||
} from 'src/types'
|
||||
|
||||
interface Props {
|
||||
notify: () => void
|
||||
notify: (message: Notification | NotificationFunc) => void
|
||||
links: AuthLinks
|
||||
meID: string
|
||||
users: User[]
|
||||
|
|
|
@ -3,11 +3,6 @@ import React, {Component} from 'react'
|
|||
import _ from 'lodash'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import {
|
||||
CellEditorOverlayActions,
|
||||
CellEditorOverlayActionsFunc,
|
||||
} from 'src/types/dashboard'
|
||||
|
||||
import ResizeContainer from 'src/shared/components/ResizeContainer'
|
||||
import QueryMaker from 'src/dashboards/components/QueryMaker'
|
||||
import Visualization from 'src/dashboards/components/Visualization'
|
||||
|
@ -15,7 +10,7 @@ import OverlayControls from 'src/dashboards/components/OverlayControls'
|
|||
import DisplayOptions from 'src/dashboards/components/DisplayOptions'
|
||||
import CEOBottom from 'src/dashboards/components/CEOBottom'
|
||||
|
||||
import * as queryModifiers from 'src/utils/queryTransitions'
|
||||
import * as queryTransitions from 'src/utils/queryTransitions'
|
||||
|
||||
import defaultQueryConfig from 'src/utils/defaultQueryConfig'
|
||||
import {buildQuery} from 'src/utils/influxql'
|
||||
|
@ -36,6 +31,9 @@ import {
|
|||
TEMP_VAR_DASHBOARD_TIME,
|
||||
} from 'src/shared/constants'
|
||||
import {getCellTypeColors} from 'src/dashboards/constants/cellEditor'
|
||||
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
import {
|
||||
TimeRange,
|
||||
Source,
|
||||
|
@ -45,7 +43,19 @@ import {
|
|||
Legend,
|
||||
Status,
|
||||
} from 'src/types'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
type QueryTransitions = typeof queryTransitions
|
||||
type EditRawTextAsyncFunc = (
|
||||
url: string,
|
||||
id: string,
|
||||
text: string
|
||||
) => Promise<void>
|
||||
type CellEditorOverlayActionsFunc = (queryID: string, ...args: any[]) => void
|
||||
type QueryActions = {
|
||||
[K in keyof QueryTransitions]: CellEditorOverlayActionsFunc
|
||||
}
|
||||
export type CellEditorOverlayActions = QueryActions & {
|
||||
editRawTextAsync: EditRawTextAsyncFunc
|
||||
}
|
||||
|
||||
const staticLegend: Legend = {
|
||||
type: 'static',
|
||||
|
@ -248,17 +258,16 @@ class CellEditorOverlay extends Component<Props, State> {
|
|||
this.overlayRef = r
|
||||
}
|
||||
|
||||
private queryStateReducer = (queryModifier): CellEditorOverlayActionsFunc => (
|
||||
queryID: string,
|
||||
...payload: any[]
|
||||
) => {
|
||||
private queryStateReducer = (
|
||||
queryTransition
|
||||
): CellEditorOverlayActionsFunc => (queryID: string, ...payload: any[]) => {
|
||||
const {queriesWorkingDraft} = this.state
|
||||
const query = queriesWorkingDraft.find(q => q.id === queryID)
|
||||
const queryWorkingDraft = queriesWorkingDraft.find(q => q.id === queryID)
|
||||
|
||||
const nextQuery = queryModifier(query, ...payload)
|
||||
const nextQuery = queryTransition(queryWorkingDraft, ...payload)
|
||||
|
||||
const nextQueries = queriesWorkingDraft.map(q => {
|
||||
if (q.id === query.id) {
|
||||
if (q.id === queryWorkingDraft.id) {
|
||||
return {...nextQuery, source: nextSource(q, nextQuery)}
|
||||
}
|
||||
|
||||
|
@ -492,20 +501,12 @@ class CellEditorOverlay extends Component<Props, State> {
|
|||
}
|
||||
|
||||
private get queryActions(): CellEditorOverlayActions {
|
||||
const original = {
|
||||
editRawTextAsync: () => Promise.resolve(),
|
||||
...queryModifiers,
|
||||
}
|
||||
const mapped = _.reduce<CellEditorOverlayActions, CellEditorOverlayActions>(
|
||||
original,
|
||||
(acc, v, k) => {
|
||||
acc[k] = this.queryStateReducer(v)
|
||||
return acc
|
||||
},
|
||||
original
|
||||
)
|
||||
const mapped: QueryActions = _.mapValues<
|
||||
QueryActions,
|
||||
CellEditorOverlayActionsFunc
|
||||
>(queryTransitions, v => this.queryStateReducer(v)) as QueryActions
|
||||
|
||||
const result = {
|
||||
const result: CellEditorOverlayActions = {
|
||||
...mapped,
|
||||
editRawTextAsync: this.handleEditRawText,
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import React, {SFC} from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
import {QueryConfig, Source, SourceLinks, TimeRange} from 'src/types'
|
||||
import {CellEditorOverlayActions} from 'src/types/dashboard'
|
||||
|
||||
import EmptyQuery from 'src/shared/components/EmptyQuery'
|
||||
import QueryTabList from 'src/shared/components/QueryTabList'
|
||||
import QueryTextArea from 'src/dashboards/components/QueryTextArea'
|
||||
|
@ -11,6 +8,9 @@ import SchemaExplorer from 'src/shared/components/SchemaExplorer'
|
|||
import {buildQuery} from 'src/utils/influxql'
|
||||
import {TYPE_QUERY_CONFIG, TEMPLATE_RANGE} from 'src/dashboards/constants'
|
||||
|
||||
import {QueryConfig, Source, SourceLinks, TimeRange} from 'src/types'
|
||||
import {CellEditorOverlayActions} from 'src/dashboards/components/CellEditorOverlay'
|
||||
|
||||
const rawTextBinder = (
|
||||
links: SourceLinks,
|
||||
id: string,
|
||||
|
|
|
@ -6,24 +6,8 @@ import FunctionSelector from 'src/shared/components/FunctionSelector'
|
|||
import {firstFieldName} from 'src/shared/reducers/helpers/fields'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
interface Field {
|
||||
type: string
|
||||
value: string
|
||||
}
|
||||
import {ApplyFuncsToFieldArgs, Field, FieldFunc, FuncArg} from 'src/types'
|
||||
|
||||
interface FuncArg {
|
||||
value: string
|
||||
type: string
|
||||
}
|
||||
|
||||
interface FieldFunc extends Field {
|
||||
args: FuncArg[]
|
||||
}
|
||||
|
||||
interface ApplyFuncsToFieldArgs {
|
||||
field: Field
|
||||
funcs: FuncArg[]
|
||||
}
|
||||
interface Props {
|
||||
fieldFuncs: FieldFunc[]
|
||||
isSelected: boolean
|
||||
|
|
|
@ -2,9 +2,10 @@ import React, {PureComponent} from 'react'
|
|||
|
||||
import QueryEditor from './QueryEditor'
|
||||
import SchemaExplorer from 'src/shared/components/SchemaExplorer'
|
||||
import {Source, QueryConfig} from 'src/types'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
import {Source, QueryConfig} from 'src/types'
|
||||
|
||||
const rawTextBinder = (links, id, action) => text =>
|
||||
action(links.queries, id, text)
|
||||
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
export const chooseNamespace = (queryID, {database, retentionPolicy}) => ({
|
||||
type: 'KAPA_CHOOSE_NAMESPACE',
|
||||
payload: {
|
||||
queryID,
|
||||
database,
|
||||
retentionPolicy,
|
||||
},
|
||||
})
|
||||
|
||||
export const chooseMeasurement = (queryID, measurement) => ({
|
||||
type: 'KAPA_CHOOSE_MEASUREMENT',
|
||||
payload: {
|
||||
queryID,
|
||||
measurement,
|
||||
},
|
||||
})
|
||||
|
||||
export const chooseTag = (queryID, tag) => ({
|
||||
type: 'KAPA_CHOOSE_TAG',
|
||||
payload: {
|
||||
queryID,
|
||||
tag,
|
||||
},
|
||||
})
|
||||
|
||||
export const groupByTag = (queryID, tagKey) => ({
|
||||
type: 'KAPA_GROUP_BY_TAG',
|
||||
payload: {
|
||||
queryID,
|
||||
tagKey,
|
||||
},
|
||||
})
|
||||
|
||||
export const toggleTagAcceptance = queryID => ({
|
||||
type: 'KAPA_TOGGLE_TAG_ACCEPTANCE',
|
||||
payload: {
|
||||
queryID,
|
||||
},
|
||||
})
|
||||
|
||||
export const toggleField = (queryID, fieldFunc) => ({
|
||||
type: 'KAPA_TOGGLE_FIELD',
|
||||
payload: {
|
||||
queryID,
|
||||
fieldFunc,
|
||||
},
|
||||
})
|
||||
|
||||
export const applyFuncsToField = (queryID, fieldFunc) => ({
|
||||
type: 'KAPA_APPLY_FUNCS_TO_FIELD',
|
||||
payload: {
|
||||
queryID,
|
||||
fieldFunc,
|
||||
},
|
||||
})
|
||||
|
||||
export const groupByTime = (queryID, time) => ({
|
||||
type: 'KAPA_GROUP_BY_TIME',
|
||||
payload: {
|
||||
queryID,
|
||||
time,
|
||||
},
|
||||
})
|
||||
|
||||
export const removeFuncs = (queryID, fields) => ({
|
||||
type: 'KAPA_REMOVE_FUNCS',
|
||||
payload: {
|
||||
queryID,
|
||||
fields,
|
||||
},
|
||||
})
|
||||
|
||||
export const timeShift = (queryID, shift) => ({
|
||||
type: 'KAPA_TIME_SHIFT',
|
||||
payload: {
|
||||
queryID,
|
||||
shift,
|
||||
},
|
||||
})
|
|
@ -0,0 +1,183 @@
|
|||
import {
|
||||
ApplyFuncsToFieldArgs,
|
||||
Field,
|
||||
Namespace,
|
||||
Tag,
|
||||
TimeShift,
|
||||
} from 'src/types'
|
||||
|
||||
interface ChooseNamespaceAction {
|
||||
type: 'KAPA_CHOOSE_NAMESPACE'
|
||||
payload: {
|
||||
queryID: string
|
||||
database: string
|
||||
retentionPolicy: string
|
||||
}
|
||||
}
|
||||
export const chooseNamespace = (
|
||||
queryID: string,
|
||||
{database, retentionPolicy}: Namespace
|
||||
): ChooseNamespaceAction => ({
|
||||
type: 'KAPA_CHOOSE_NAMESPACE',
|
||||
payload: {
|
||||
queryID,
|
||||
database,
|
||||
retentionPolicy,
|
||||
},
|
||||
})
|
||||
|
||||
interface ChooseMeasurementAction {
|
||||
type: 'KAPA_CHOOSE_MEASUREMENT'
|
||||
payload: {
|
||||
queryID: string
|
||||
measurement: string
|
||||
}
|
||||
}
|
||||
export const chooseMeasurement = (
|
||||
queryID: string,
|
||||
measurement: string
|
||||
): ChooseMeasurementAction => ({
|
||||
type: 'KAPA_CHOOSE_MEASUREMENT',
|
||||
payload: {
|
||||
queryID,
|
||||
measurement,
|
||||
},
|
||||
})
|
||||
|
||||
interface ChooseTagAction {
|
||||
type: 'KAPA_CHOOSE_TAG'
|
||||
payload: {
|
||||
queryID: string
|
||||
tag: Tag
|
||||
}
|
||||
}
|
||||
export const chooseTag = (queryID: string, tag: Tag): ChooseTagAction => ({
|
||||
type: 'KAPA_CHOOSE_TAG',
|
||||
payload: {
|
||||
queryID,
|
||||
tag,
|
||||
},
|
||||
})
|
||||
|
||||
interface GroupByTagAction {
|
||||
type: 'KAPA_GROUP_BY_TAG'
|
||||
payload: {
|
||||
queryID: string
|
||||
tagKey: string
|
||||
}
|
||||
}
|
||||
export const groupByTag = (
|
||||
queryID: string,
|
||||
tagKey: string
|
||||
): GroupByTagAction => ({
|
||||
type: 'KAPA_GROUP_BY_TAG',
|
||||
payload: {
|
||||
queryID,
|
||||
tagKey,
|
||||
},
|
||||
})
|
||||
|
||||
interface ToggleTagAcceptanceAction {
|
||||
type: 'KAPA_TOGGLE_TAG_ACCEPTANCE'
|
||||
payload: {
|
||||
queryID: string
|
||||
}
|
||||
}
|
||||
export const toggleTagAcceptance = (
|
||||
queryID: string
|
||||
): ToggleTagAcceptanceAction => ({
|
||||
type: 'KAPA_TOGGLE_TAG_ACCEPTANCE',
|
||||
payload: {
|
||||
queryID,
|
||||
},
|
||||
})
|
||||
|
||||
interface ToggleFieldAction {
|
||||
type: 'KAPA_TOGGLE_FIELD'
|
||||
payload: {
|
||||
queryID: string
|
||||
fieldFunc: Field
|
||||
}
|
||||
}
|
||||
export const toggleField = (
|
||||
queryID: string,
|
||||
fieldFunc: Field
|
||||
): ToggleFieldAction => ({
|
||||
type: 'KAPA_TOGGLE_FIELD',
|
||||
payload: {
|
||||
queryID,
|
||||
fieldFunc,
|
||||
},
|
||||
})
|
||||
|
||||
interface ApplyFuncsToFieldAction {
|
||||
type: 'KAPA_APPLY_FUNCS_TO_FIELD'
|
||||
payload: {
|
||||
queryID: string
|
||||
fieldFunc: ApplyFuncsToFieldArgs
|
||||
}
|
||||
}
|
||||
export const applyFuncsToField = (
|
||||
queryID: string,
|
||||
fieldFunc: ApplyFuncsToFieldArgs
|
||||
): ApplyFuncsToFieldAction => ({
|
||||
type: 'KAPA_APPLY_FUNCS_TO_FIELD',
|
||||
payload: {
|
||||
queryID,
|
||||
fieldFunc,
|
||||
},
|
||||
})
|
||||
|
||||
interface GroupByTimeAction {
|
||||
type: 'KAPA_GROUP_BY_TIME'
|
||||
payload: {
|
||||
queryID: string
|
||||
time: string
|
||||
}
|
||||
}
|
||||
export const groupByTime = (
|
||||
queryID: string,
|
||||
time: string
|
||||
): GroupByTimeAction => ({
|
||||
type: 'KAPA_GROUP_BY_TIME',
|
||||
payload: {
|
||||
queryID,
|
||||
time,
|
||||
},
|
||||
})
|
||||
|
||||
interface RemoveFuncsAction {
|
||||
type: 'KAPA_REMOVE_FUNCS'
|
||||
payload: {
|
||||
queryID: string
|
||||
fields: Field[]
|
||||
}
|
||||
}
|
||||
export const removeFuncs = (
|
||||
queryID: string,
|
||||
fields: Field[]
|
||||
): RemoveFuncsAction => ({
|
||||
type: 'KAPA_REMOVE_FUNCS',
|
||||
payload: {
|
||||
queryID,
|
||||
fields,
|
||||
},
|
||||
})
|
||||
|
||||
interface TimeShiftAction {
|
||||
type: 'KAPA_TIME_SHIFT'
|
||||
payload: {
|
||||
queryID: string
|
||||
shift: TimeShift
|
||||
}
|
||||
}
|
||||
export const timeShift = (
|
||||
queryID: string,
|
||||
shift: TimeShift
|
||||
): TimeShiftAction => ({
|
||||
type: 'KAPA_TIME_SHIFT',
|
||||
payload: {
|
||||
queryID,
|
||||
shift,
|
||||
},
|
||||
})
|
|
@ -2,17 +2,7 @@ import React, {SFC} from 'react'
|
|||
|
||||
import AlertTabs from 'src/kapacitor/components/AlertTabs'
|
||||
|
||||
import {Kapacitor, Source} from 'src/types'
|
||||
|
||||
export interface Notification {
|
||||
id?: string
|
||||
type: string
|
||||
icon: string
|
||||
duration: number
|
||||
message: string
|
||||
}
|
||||
|
||||
type NotificationFunc = () => Notification
|
||||
import {Kapacitor, Source, Notification, NotificationFunc} from 'src/types'
|
||||
|
||||
interface AlertOutputProps {
|
||||
exists: boolean
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
import {
|
||||
AlertaConfig,
|
||||
HipChatConfig,
|
||||
KafkaConfig,
|
||||
OpsGenieConfig,
|
||||
PagerDutyConfig,
|
||||
PushoverConfig,
|
||||
|
@ -72,6 +73,7 @@ interface Sections {
|
|||
hipchat: Section
|
||||
httppost: Section
|
||||
influxdb: Section
|
||||
kafka: Section
|
||||
mqtt: Section
|
||||
opsgenie: Section
|
||||
opsgenie2: Section
|
||||
|
@ -96,6 +98,7 @@ interface Config {
|
|||
interface SupportedConfig {
|
||||
alerta: Config
|
||||
hipchat: Config
|
||||
kafka: Config
|
||||
opsgenie: Config
|
||||
opsgenie2: Config
|
||||
pagerduty: Config
|
||||
|
@ -222,6 +225,21 @@ class AlertTabs extends PureComponent<Props, State> {
|
|||
/>
|
||||
),
|
||||
},
|
||||
kafka: {
|
||||
type: 'Kafka',
|
||||
enabled: this.getEnabled(configSections, 'kafka'),
|
||||
renderComponent: () => (
|
||||
<KafkaConfig
|
||||
onSave={this.handleSaveConfig('kafka')}
|
||||
config={this.getSection(configSections, 'kafka')}
|
||||
onTest={this.handleTestConfig('kafka', {
|
||||
cluster: this.getProperty(configSections, 'kafka', 'id'),
|
||||
})}
|
||||
enabled={this.getEnabled(configSections, 'kafka')}
|
||||
notify={this.props.notify}
|
||||
/>
|
||||
),
|
||||
},
|
||||
opsgenie: {
|
||||
type: 'OpsGenie',
|
||||
enabled: this.getEnabled(configSections, 'opsgenie'),
|
||||
|
@ -434,6 +452,18 @@ class AlertTabs extends PureComponent<Props, State> {
|
|||
)
|
||||
}
|
||||
|
||||
private getProperty = (
|
||||
sections: Sections,
|
||||
section: string,
|
||||
property: string
|
||||
): boolean => {
|
||||
return _.get(
|
||||
sections,
|
||||
[section, 'elements', '0', 'options', property],
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
private handleSaveConfig = (section: string) => async (
|
||||
properties
|
||||
): Promise<boolean> => {
|
||||
|
@ -451,19 +481,23 @@ class AlertTabs extends PureComponent<Props, State> {
|
|||
} catch ({
|
||||
data: {error},
|
||||
}) {
|
||||
const errorMsg = _.join(_.drop(_.split(error, ': '), 2), ': ')
|
||||
const errorMsg = error.split(': ').pop()
|
||||
this.props.notify(notifyAlertEndpointSaveFailed(section, errorMsg))
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
private handleTestConfig = (section: string) => async (
|
||||
private handleTestConfig = (section: string, options?: object) => async (
|
||||
e: MouseEvent<HTMLButtonElement>
|
||||
): Promise<void> => {
|
||||
e.preventDefault()
|
||||
|
||||
try {
|
||||
const {data} = await testAlertOutput(this.props.kapacitor, section)
|
||||
const {data} = await testAlertOutput(
|
||||
this.props.kapacitor,
|
||||
section,
|
||||
options
|
||||
)
|
||||
if (data.success) {
|
||||
this.props.notify(notifyTestAlertSent(section))
|
||||
} else {
|
||||
|
|
|
@ -1,126 +0,0 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import DatabaseList from 'src/shared/components/DatabaseList'
|
||||
import MeasurementList from 'src/shared/components/MeasurementList'
|
||||
import FieldList from 'src/shared/components/FieldList'
|
||||
|
||||
import {defaultEveryFrequency} from 'src/kapacitor/constants'
|
||||
|
||||
import {SourceContext} from 'src/CheckSources'
|
||||
|
||||
const makeQueryHandlers = (actions, query) => ({
|
||||
handleChooseNamespace: namespace => {
|
||||
actions.chooseNamespace(query.id, namespace)
|
||||
},
|
||||
|
||||
handleChooseMeasurement: measurement => {
|
||||
actions.chooseMeasurement(query.id, measurement)
|
||||
},
|
||||
|
||||
handleToggleField: field => {
|
||||
actions.toggleField(query.id, field)
|
||||
},
|
||||
|
||||
handleGroupByTime: time => {
|
||||
actions.groupByTime(query.id, time)
|
||||
},
|
||||
|
||||
handleApplyFuncsToField: onAddEvery => fieldFunc => {
|
||||
actions.applyFuncsToField(query.id, fieldFunc)
|
||||
onAddEvery(defaultEveryFrequency)
|
||||
},
|
||||
|
||||
handleChooseTag: tag => {
|
||||
actions.chooseTag(query.id, tag)
|
||||
},
|
||||
|
||||
handleToggleTagAcceptance: () => {
|
||||
actions.toggleTagAcceptance(query.id)
|
||||
},
|
||||
|
||||
handleGroupByTag: tagKey => {
|
||||
actions.groupByTag(query.id, tagKey)
|
||||
},
|
||||
|
||||
handleRemoveFuncs: fields => {
|
||||
actions.removeFuncs(query.id, fields)
|
||||
},
|
||||
})
|
||||
|
||||
const DataSection = ({
|
||||
actions,
|
||||
query,
|
||||
isDeadman,
|
||||
isKapacitorRule,
|
||||
onAddEvery,
|
||||
}) => {
|
||||
const {
|
||||
handleChooseTag,
|
||||
handleGroupByTag,
|
||||
handleToggleField,
|
||||
handleGroupByTime,
|
||||
handleRemoveFuncs,
|
||||
handleChooseNamespace,
|
||||
handleApplyFuncsToField,
|
||||
handleChooseMeasurement,
|
||||
handleToggleTagAcceptance,
|
||||
} = makeQueryHandlers(actions, query)
|
||||
|
||||
return (
|
||||
<SourceContext.Consumer>
|
||||
{source => (
|
||||
<div className="rule-section">
|
||||
<div className="query-builder">
|
||||
<DatabaseList
|
||||
query={query}
|
||||
onChooseNamespace={handleChooseNamespace}
|
||||
/>
|
||||
<MeasurementList
|
||||
query={query}
|
||||
onChooseMeasurement={handleChooseMeasurement}
|
||||
onChooseTag={handleChooseTag}
|
||||
onGroupByTag={handleGroupByTag}
|
||||
onToggleTagAcceptance={handleToggleTagAcceptance}
|
||||
/>
|
||||
{isDeadman ? null : (
|
||||
<FieldList
|
||||
query={query}
|
||||
onToggleField={handleToggleField}
|
||||
isKapacitorRule={isKapacitorRule}
|
||||
onGroupByTime={handleGroupByTime}
|
||||
removeFuncs={handleRemoveFuncs}
|
||||
applyFuncsToField={handleApplyFuncsToField(onAddEvery)}
|
||||
source={source}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</SourceContext.Consumer>
|
||||
)
|
||||
}
|
||||
|
||||
const {bool, func, shape, string} = PropTypes
|
||||
|
||||
DataSection.propTypes = {
|
||||
query: shape({
|
||||
id: string.isRequired,
|
||||
}).isRequired,
|
||||
actions: shape({
|
||||
chooseNamespace: func.isRequired,
|
||||
chooseMeasurement: func.isRequired,
|
||||
applyFuncsToField: func.isRequired,
|
||||
chooseTag: func.isRequired,
|
||||
groupByTag: func.isRequired,
|
||||
toggleField: func.isRequired,
|
||||
groupByTime: func.isRequired,
|
||||
toggleTagAcceptance: func.isRequired,
|
||||
}).isRequired,
|
||||
onAddEvery: func.isRequired,
|
||||
timeRange: shape({}).isRequired,
|
||||
isKapacitorRule: bool,
|
||||
isDeadman: bool,
|
||||
}
|
||||
|
||||
export default DataSection
|
|
@ -0,0 +1,119 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
|
||||
import DatabaseList from 'src/shared/components/DatabaseList'
|
||||
import MeasurementList from 'src/shared/components/MeasurementList'
|
||||
import FieldList from 'src/shared/components/FieldList'
|
||||
|
||||
import {defaultEveryFrequency} from 'src/kapacitor/constants'
|
||||
|
||||
import {SourceContext} from 'src/CheckSources'
|
||||
|
||||
import {
|
||||
ApplyFuncsToFieldArgs,
|
||||
Field,
|
||||
Namespace,
|
||||
QueryConfig,
|
||||
Source,
|
||||
TimeRange,
|
||||
Tag,
|
||||
} from 'src/types'
|
||||
import {KapacitorQueryConfigActions} from 'src/types/actions'
|
||||
|
||||
interface Props {
|
||||
actions: KapacitorQueryConfigActions
|
||||
query: QueryConfig
|
||||
isDeadman: boolean
|
||||
isKapacitorRule: boolean
|
||||
onAddEvery: () => void
|
||||
timeRange: TimeRange
|
||||
}
|
||||
|
||||
class DataSection extends PureComponent<Props> {
|
||||
public render() {
|
||||
const {query, isDeadman, isKapacitorRule, onAddEvery} = this.props
|
||||
|
||||
return (
|
||||
<SourceContext.Consumer>
|
||||
{(source: Source) => (
|
||||
<div className="rule-section">
|
||||
<div className="query-builder">
|
||||
<DatabaseList
|
||||
query={query}
|
||||
onChooseNamespace={this.handleChooseNamespace}
|
||||
/>
|
||||
<MeasurementList
|
||||
query={query}
|
||||
onChooseMeasurement={this.handleChooseMeasurement}
|
||||
onChooseTag={this.handleChooseTag}
|
||||
onGroupByTag={this.handleGroupByTag}
|
||||
onToggleTagAcceptance={this.handleToggleTagAcceptance}
|
||||
isKapacitorRule={isKapacitorRule}
|
||||
/>
|
||||
{isDeadman ? null : (
|
||||
<FieldList
|
||||
query={query}
|
||||
applyFuncsToField={this.handleApplyFuncsToField(onAddEvery)}
|
||||
onGroupByTime={this.handleGroupByTime}
|
||||
onToggleField={this.handleToggleField}
|
||||
removeFuncs={this.handleRemoveFuncs}
|
||||
isKapacitorRule={isKapacitorRule}
|
||||
source={source}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</SourceContext.Consumer>
|
||||
)
|
||||
}
|
||||
|
||||
private handleChooseNamespace = (namespace: Namespace): void => {
|
||||
const {actions, query} = this.props
|
||||
actions.chooseNamespace(query.id, namespace)
|
||||
}
|
||||
|
||||
private handleChooseMeasurement = (measurement: string): void => {
|
||||
const {actions, query} = this.props
|
||||
actions.chooseMeasurement(query.id, measurement)
|
||||
}
|
||||
|
||||
private handleToggleField = (field: Field): void => {
|
||||
const {actions, query} = this.props
|
||||
actions.toggleField(query.id, field)
|
||||
}
|
||||
|
||||
private handleGroupByTime = (time: string): void => {
|
||||
const {actions, query} = this.props
|
||||
actions.groupByTime(query.id, time)
|
||||
}
|
||||
|
||||
private handleApplyFuncsToField = (onAddEvery: (every: string) => void) => (
|
||||
fieldFunc: ApplyFuncsToFieldArgs
|
||||
): void => {
|
||||
const {actions, query} = this.props
|
||||
actions.applyFuncsToField(query.id, fieldFunc)
|
||||
onAddEvery(defaultEveryFrequency)
|
||||
}
|
||||
|
||||
private handleChooseTag = (tag: Tag): void => {
|
||||
const {actions, query} = this.props
|
||||
actions.chooseTag(query.id, tag)
|
||||
}
|
||||
|
||||
private handleToggleTagAcceptance = (): void => {
|
||||
const {actions, query} = this.props
|
||||
actions.toggleTagAcceptance(query.id)
|
||||
}
|
||||
|
||||
private handleGroupByTag = (tagKey: string): void => {
|
||||
const {actions, query} = this.props
|
||||
actions.groupByTag(query.id, tagKey)
|
||||
}
|
||||
|
||||
private handleRemoveFuncs = (fields: Field[]): void => {
|
||||
const {actions, query} = this.props
|
||||
actions.removeFuncs(query.id, fields)
|
||||
}
|
||||
}
|
||||
|
||||
export default DataSection
|
|
@ -8,6 +8,7 @@ import {
|
|||
EmailHandler,
|
||||
AlertaHandler,
|
||||
HipchatHandler,
|
||||
KafkaHandler,
|
||||
OpsgenieHandler,
|
||||
PagerdutyHandler,
|
||||
PushoverHandler,
|
||||
|
@ -92,6 +93,15 @@ class HandlerOptions extends Component {
|
|||
validationError={validationError}
|
||||
/>
|
||||
)
|
||||
case 'kafka':
|
||||
return (
|
||||
<KafkaHandler
|
||||
selectedHandler={selectedHandler}
|
||||
handleModifyHandler={handleModifyHandler}
|
||||
onGoToConfig={onGoToConfig('kafka')}
|
||||
validationError={validationError}
|
||||
/>
|
||||
)
|
||||
case 'opsGenie':
|
||||
return (
|
||||
<OpsgenieHandler
|
||||
|
|
|
@ -3,19 +3,9 @@ import React, {ChangeEvent, MouseEvent, PureComponent} from 'react'
|
|||
import AlertOutputs from 'src/kapacitor/components/AlertOutputs'
|
||||
import Input from 'src/kapacitor/components/KapacitorFormInput'
|
||||
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||
|
||||
import {Kapacitor, Source} from 'src/types'
|
||||
import KapacitorFormSkipVerify from 'src/kapacitor/components/KapacitorFormSkipVerify'
|
||||
|
||||
export interface Notification {
|
||||
id?: string
|
||||
type: string
|
||||
icon: string
|
||||
duration: number
|
||||
message: string
|
||||
}
|
||||
|
||||
type NotificationFunc = () => Notification
|
||||
import {Kapacitor, Source, Notification, NotificationFunc} from 'src/types'
|
||||
|
||||
interface Props {
|
||||
kapacitor: Kapacitor
|
||||
|
|
|
@ -0,0 +1,261 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
|
||||
import TagInput from 'src/shared/components/TagInput'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
import {Notification, NotificationFunc} from 'src/types'
|
||||
|
||||
import {notifyInvalidBatchSizeValue} from 'src/shared/copy/notifications'
|
||||
|
||||
interface Properties {
|
||||
brokers: string[]
|
||||
timeout: string
|
||||
'batch-size': number
|
||||
'batch-timeout': string
|
||||
'use-ssl': boolean
|
||||
'ssl-ca': string
|
||||
'ssl-cert': string
|
||||
'ssl-key': string
|
||||
'insecure-skip-verify': boolean
|
||||
}
|
||||
|
||||
interface Config {
|
||||
options: Properties & {
|
||||
id: string
|
||||
}
|
||||
}
|
||||
|
||||
interface Item {
|
||||
name?: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
config: Config
|
||||
onSave: (properties: Properties) => void
|
||||
onTest: (event: React.MouseEvent<HTMLButtonElement>) => void
|
||||
enabled: boolean
|
||||
notify: (message: Notification | NotificationFunc) => void
|
||||
}
|
||||
|
||||
interface State {
|
||||
currentBrokers: string[]
|
||||
testEnabled: boolean
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
class KafkaConfig extends PureComponent<Props, State> {
|
||||
private id: HTMLInputElement
|
||||
private timeout: HTMLInputElement
|
||||
private batchSize: HTMLInputElement
|
||||
private batchTimeout: HTMLInputElement
|
||||
private useSSL: HTMLInputElement
|
||||
private sslCA: HTMLInputElement
|
||||
private sslCert: HTMLInputElement
|
||||
private sslKey: HTMLInputElement
|
||||
private insecureSkipVerify: HTMLInputElement
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const {brokers} = props.config.options
|
||||
|
||||
this.state = {
|
||||
currentBrokers: brokers || [],
|
||||
testEnabled: this.props.enabled,
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {options} = this.props.config
|
||||
const id = options.id
|
||||
const timeout = options.timeout
|
||||
const batchSize = options['batch-size']
|
||||
const batchTimeout = options['batch-timeout']
|
||||
const useSSL = options['use-ssl']
|
||||
const sslCA = options['ssl-ca']
|
||||
const sslCert = options['ssl-cert']
|
||||
const sslKey = options['ssl-key']
|
||||
const insecureSkipVerify = options['insecure-skip-verify']
|
||||
|
||||
return (
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="id">ID</label>
|
||||
<input
|
||||
className="form-control"
|
||||
id="id"
|
||||
type="text"
|
||||
ref={r => (this.id = r)}
|
||||
defaultValue={id || ''}
|
||||
onChange={this.disableTest}
|
||||
readOnly={true}
|
||||
/>
|
||||
</div>
|
||||
<TagInput
|
||||
title="Brokers"
|
||||
onAddTag={this.handleAddBroker}
|
||||
onDeleteTag={this.handleDeleteBroker}
|
||||
tags={this.currentBrokersForTags}
|
||||
disableTest={this.disableTest}
|
||||
/>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="timeout">Timeout</label>
|
||||
<input
|
||||
className="form-control"
|
||||
id="timeout"
|
||||
type="text"
|
||||
ref={r => (this.timeout = r)}
|
||||
defaultValue={timeout || ''}
|
||||
onChange={this.disableTest}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="batchSize">Batch Size</label>
|
||||
<input
|
||||
className="form-control"
|
||||
id="batchSize"
|
||||
type="number"
|
||||
ref={r => (this.batchSize = r)}
|
||||
defaultValue={batchSize.toString() || '0'}
|
||||
onChange={this.disableTest}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="batchTimeout">Batch Timeout</label>
|
||||
<input
|
||||
className="form-control"
|
||||
id="batchTimeout"
|
||||
type="text"
|
||||
ref={r => (this.batchTimeout = r)}
|
||||
defaultValue={batchTimeout || ''}
|
||||
onChange={this.disableTest}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="form-control-static">
|
||||
<input
|
||||
id="useSSL"
|
||||
type="checkbox"
|
||||
defaultChecked={useSSL}
|
||||
ref={r => (this.useSSL = r)}
|
||||
onChange={this.disableTest}
|
||||
/>
|
||||
<label htmlFor="useSSL">Use SSL</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="sslCA">SSL CA</label>
|
||||
<input
|
||||
className="form-control"
|
||||
id="sslCA"
|
||||
type="text"
|
||||
ref={r => (this.sslCA = r)}
|
||||
defaultValue={sslCA || ''}
|
||||
onChange={this.disableTest}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="sslCert">SSL Cert</label>
|
||||
<input
|
||||
className="form-control"
|
||||
id="sslCert"
|
||||
type="text"
|
||||
ref={r => (this.sslCert = r)}
|
||||
defaultValue={sslCert || ''}
|
||||
onChange={this.disableTest}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="sslKey">SSL Key</label>
|
||||
<input
|
||||
className="form-control"
|
||||
id="sslKey"
|
||||
type="text"
|
||||
ref={r => (this.sslKey = r)}
|
||||
defaultValue={sslKey || ''}
|
||||
onChange={this.disableTest}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="form-control-static">
|
||||
<input
|
||||
id="insecureSkipVerify"
|
||||
type="checkbox"
|
||||
defaultChecked={insecureSkipVerify}
|
||||
ref={r => (this.insecureSkipVerify = r)}
|
||||
onChange={this.disableTest}
|
||||
/>
|
||||
<label htmlFor="insecureSkipVerify">Insecure Skip Verify</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group form-group-submit col-xs-12 text-center">
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
type="submit"
|
||||
disabled={this.state.testEnabled}
|
||||
>
|
||||
<span className="icon checkmark" />
|
||||
Save Changes
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
disabled={!this.state.testEnabled}
|
||||
onClick={this.props.onTest}
|
||||
>
|
||||
<span className="icon pulse-c" />
|
||||
Send Test Alert
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
private get currentBrokersForTags(): Item[] {
|
||||
const {currentBrokers} = this.state
|
||||
return currentBrokers.map(broker => ({name: broker}))
|
||||
}
|
||||
|
||||
private handleSubmit = async e => {
|
||||
e.preventDefault()
|
||||
|
||||
const batchSize = parseInt(this.batchSize.value, 10)
|
||||
if (isNaN(batchSize)) {
|
||||
this.props.notify(notifyInvalidBatchSizeValue())
|
||||
return
|
||||
}
|
||||
|
||||
const properties = {
|
||||
brokers: this.state.currentBrokers,
|
||||
timeout: this.timeout.value,
|
||||
'batch-size': batchSize,
|
||||
'batch-timeout': this.batchTimeout.value,
|
||||
'use-ssl': this.useSSL.checked,
|
||||
'ssl-ca': this.sslCA.value,
|
||||
'ssl-cert': this.sslCert.value,
|
||||
'ssl-key': this.sslKey.value,
|
||||
'insecure-skip-verify': this.insecureSkipVerify.checked,
|
||||
}
|
||||
|
||||
const success = await this.props.onSave(properties)
|
||||
if (success) {
|
||||
this.setState({testEnabled: true})
|
||||
}
|
||||
}
|
||||
|
||||
private disableTest = () => {
|
||||
this.setState({testEnabled: false})
|
||||
}
|
||||
|
||||
private handleAddBroker = broker => {
|
||||
this.setState({currentBrokers: this.state.currentBrokers.concat(broker)})
|
||||
}
|
||||
|
||||
private handleDeleteBroker = broker => {
|
||||
this.setState({
|
||||
currentBrokers: this.state.currentBrokers.filter(t => t !== broker.name),
|
||||
testEnabled: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default KafkaConfig
|
|
@ -1,5 +1,6 @@
|
|||
import AlertaConfig from './AlertaConfig'
|
||||
import HipChatConfig from './HipChatConfig'
|
||||
import KafkaConfig from './KafkaConfig'
|
||||
import OpsGenieConfig from './OpsGenieConfig'
|
||||
import PagerDutyConfig from './PagerDutyConfig'
|
||||
import PushoverConfig from './PushoverConfig'
|
||||
|
@ -13,6 +14,7 @@ import VictorOpsConfig from './VictorOpsConfig'
|
|||
export {
|
||||
AlertaConfig,
|
||||
HipChatConfig,
|
||||
KafkaConfig,
|
||||
OpsGenieConfig,
|
||||
PagerDutyConfig,
|
||||
PushoverConfig,
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
import React, {SFC} from 'react'
|
||||
|
||||
import HandlerInput from 'src/kapacitor/components/HandlerInput'
|
||||
|
||||
interface Handler {
|
||||
alias: string
|
||||
enabled: boolean
|
||||
headerKey: string
|
||||
headerValue: string
|
||||
headers: {
|
||||
[key: string]: string
|
||||
}
|
||||
text: string
|
||||
type: string
|
||||
url: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
selectedHandler: object
|
||||
handleModifyHandler: (
|
||||
selectedHandler: Handler,
|
||||
fieldName: string,
|
||||
parseToArray: string
|
||||
) => void
|
||||
}
|
||||
|
||||
const KafkaHandler: SFC<Props> = ({selectedHandler, handleModifyHandler}) => (
|
||||
<div className="endpoint-tab-contents">
|
||||
<div className="endpoint-tab--parameters">
|
||||
<h4>Parameters for this Alert Handler</h4>
|
||||
<div className="faux-form">
|
||||
<HandlerInput
|
||||
selectedHandler={selectedHandler}
|
||||
handleModifyHandler={handleModifyHandler}
|
||||
fieldName="cluster"
|
||||
fieldDisplay="Cluster"
|
||||
placeholder=""
|
||||
fieldColumns="col-md-12"
|
||||
/>
|
||||
<HandlerInput
|
||||
selectedHandler={selectedHandler}
|
||||
handleModifyHandler={handleModifyHandler}
|
||||
fieldName="topic"
|
||||
fieldDisplay="Topic"
|
||||
placeholder=""
|
||||
/>
|
||||
<HandlerInput
|
||||
selectedHandler={selectedHandler}
|
||||
handleModifyHandler={handleModifyHandler}
|
||||
fieldName="template"
|
||||
fieldDisplay="Template"
|
||||
placeholder=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default KafkaHandler
|
|
@ -4,6 +4,7 @@ import ExecHandler from './ExecHandler'
|
|||
import LogHandler from './LogHandler'
|
||||
import AlertaHandler from './AlertaHandler'
|
||||
import HipchatHandler from './HipchatHandler'
|
||||
import KafkaHandler from './KafkaHandler'
|
||||
import OpsgenieHandler from './OpsgenieHandler'
|
||||
import PagerdutyHandler from './PagerdutyHandler'
|
||||
import PushoverHandler from './PushoverHandler'
|
||||
|
@ -22,6 +23,7 @@ export {
|
|||
EmailHandler,
|
||||
AlertaHandler,
|
||||
HipchatHandler,
|
||||
KafkaHandler,
|
||||
OpsgenieHandler,
|
||||
PagerdutyHandler,
|
||||
PushoverHandler,
|
||||
|
|
|
@ -114,6 +114,7 @@ export const MAP_KEYS_FROM_CONFIG = {
|
|||
export const ALERTS_FROM_CONFIG = {
|
||||
alerta: ['environment', 'origin', 'token'], // token = bool
|
||||
hipChat: ['url', 'room', 'token'], // token = bool
|
||||
kafka: [],
|
||||
opsGenie: ['api-key', 'teams', 'recipients'], // api-key = bool
|
||||
opsGenie2: ['api-key', 'teams', 'recipients'], // api-key = bool
|
||||
pagerDuty: ['service-key'], // service-key = bool
|
||||
|
@ -172,6 +173,7 @@ export const HANDLERS_TO_RULE = {
|
|||
'service',
|
||||
],
|
||||
hipChat: ['room'],
|
||||
kafka: ['cluster', 'topic', 'template'],
|
||||
opsGenie: ['teams', 'recipients'],
|
||||
opsGenie2: ['teams', 'recipients'],
|
||||
pagerDuty: [],
|
||||
|
|
|
@ -5,7 +5,7 @@ import {bindActionCreators} from 'redux'
|
|||
|
||||
import {notify as notifyAction} from 'src/shared/actions/notifications'
|
||||
|
||||
import {Source} from 'src/types'
|
||||
import {Source, Notification, NotificationFunc} from 'src/types'
|
||||
|
||||
import {
|
||||
createKapacitor,
|
||||
|
@ -29,16 +29,6 @@ import {ErrorHandling} from 'src/shared/decorators/errors'
|
|||
export const defaultName = 'My Kapacitor'
|
||||
export const kapacitorPort = '9092'
|
||||
|
||||
export interface Notification {
|
||||
id?: string
|
||||
type: string
|
||||
icon: string
|
||||
duration: number
|
||||
message: string
|
||||
}
|
||||
|
||||
export type NotificationFunc = () => Notification
|
||||
|
||||
interface Kapacitor {
|
||||
url: string
|
||||
name: string
|
||||
|
|
|
@ -10,7 +10,14 @@ import {getActiveKapacitor} from 'src/shared/apis'
|
|||
import {getLogStreamByRuleID, pingKapacitorVersion} from 'src/kapacitor/apis'
|
||||
import {notify as notifyAction} from 'src/shared/actions/notifications'
|
||||
|
||||
import {Source, Kapacitor, Task, AlertRule} from 'src/types'
|
||||
import {
|
||||
Source,
|
||||
Kapacitor,
|
||||
Task,
|
||||
AlertRule,
|
||||
Notification,
|
||||
NotificationFunc,
|
||||
} from 'src/types'
|
||||
|
||||
import {
|
||||
notifyTickscriptLoggingUnavailable,
|
||||
|
@ -61,7 +68,7 @@ interface Props {
|
|||
router: Router
|
||||
params: Params
|
||||
rules: AlertRule[]
|
||||
notify: any
|
||||
notify: (message: Notification | NotificationFunc) => void
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
|
|
@ -173,13 +173,13 @@ export function updateKapacitorConfigSection(kapacitor, section, properties) {
|
|||
return AJAX(params)
|
||||
}
|
||||
|
||||
export const testAlertOutput = async (kapacitor, outputName) => {
|
||||
export const testAlertOutput = async (kapacitor, outputName, options) => {
|
||||
try {
|
||||
const {
|
||||
data: {services},
|
||||
} = await kapacitorProxy(kapacitor, 'GET', '/kapacitor/v1/service-tests')
|
||||
const service = services.find(s => s.name === outputName)
|
||||
return kapacitorProxy(kapacitor, 'POST', service.link.href, {})
|
||||
return kapacitorProxy(kapacitor, 'POST', service.link.href, options)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import _ from 'lodash'
|
||||
|
@ -16,9 +16,9 @@ import {ErrorHandling} from 'src/shared/decorators/errors'
|
|||
|
||||
interface DatabaseListProps {
|
||||
query: QueryConfig
|
||||
querySource: Source
|
||||
querySource?: Source
|
||||
onChooseNamespace: (namespace: Namespace) => void
|
||||
source: Source
|
||||
source?: Source
|
||||
}
|
||||
|
||||
interface DatabaseListState {
|
||||
|
@ -28,7 +28,7 @@ interface DatabaseListState {
|
|||
const {shape} = PropTypes
|
||||
|
||||
@ErrorHandling
|
||||
class DatabaseList extends PureComponent<DatabaseListProps, DatabaseListState> {
|
||||
class DatabaseList extends Component<DatabaseListProps, DatabaseListState> {
|
||||
public static contextTypes = {
|
||||
source: shape({
|
||||
links: shape({}).isRequired,
|
||||
|
@ -52,7 +52,13 @@ class DatabaseList extends PureComponent<DatabaseListProps, DatabaseListState> {
|
|||
this.getDbRp()
|
||||
}
|
||||
|
||||
public componentDidUpdate({querySource: prevSource, query: prevQuery}) {
|
||||
public componentDidUpdate({
|
||||
querySource: prevSource,
|
||||
query: prevQuery,
|
||||
}: {
|
||||
querySource?: Source
|
||||
query: QueryConfig
|
||||
}) {
|
||||
const {querySource: nextSource, query: nextQuery} = this.props
|
||||
const differentSource = !_.isEqual(prevSource, nextSource)
|
||||
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
import {QueryConfig, GroupBy, Source, TimeShift} from 'src/types'
|
||||
import {
|
||||
ApplyFuncsToFieldArgs,
|
||||
Field,
|
||||
FieldFunc,
|
||||
GroupBy,
|
||||
QueryConfig,
|
||||
Source,
|
||||
TimeShift,
|
||||
} from 'src/types'
|
||||
|
||||
import QueryOptions from 'src/shared/components/QueryOptions'
|
||||
import FieldListItem from 'src/data_explorer/components/FieldListItem'
|
||||
|
@ -28,39 +36,21 @@ interface Links {
|
|||
proxy: string
|
||||
}
|
||||
|
||||
interface Field {
|
||||
type: string
|
||||
value: string
|
||||
}
|
||||
|
||||
interface FieldFunc extends Field {
|
||||
args: FuncArg[]
|
||||
}
|
||||
interface FuncArg {
|
||||
value: string
|
||||
type: string
|
||||
}
|
||||
|
||||
interface ApplyFuncsToFieldArgs {
|
||||
field: Field
|
||||
funcs: FuncArg[]
|
||||
}
|
||||
|
||||
interface Props {
|
||||
query: QueryConfig
|
||||
onTimeShift: (shift: TimeShiftOption) => void
|
||||
onToggleField: (field: Field) => void
|
||||
addInitialField?: (field: Field, groupBy: GroupBy) => void
|
||||
applyFuncsToField: (field: ApplyFuncsToFieldArgs, groupBy?: GroupBy) => void
|
||||
onFill?: (fill: string) => void
|
||||
onGroupByTime: (groupByOption: string) => void
|
||||
onFill: (fill: string) => void
|
||||
applyFuncsToField: (field: ApplyFuncsToFieldArgs, groupBy: GroupBy) => void
|
||||
onTimeShift?: (shift: TimeShiftOption) => void
|
||||
onToggleField: (field: Field) => void
|
||||
removeFuncs: (fields: Field[]) => void
|
||||
isKapacitorRule: boolean
|
||||
querySource: {
|
||||
querySource?: {
|
||||
links: Links
|
||||
}
|
||||
removeFuncs: (fields: Field[]) => void
|
||||
addInitialField: (field: Field, groupBy: GroupBy) => void
|
||||
initialGroupByTime: string | null
|
||||
isQuerySupportedByExplorer: boolean
|
||||
initialGroupByTime?: string | null
|
||||
isQuerySupportedByExplorer?: boolean
|
||||
source: Source
|
||||
}
|
||||
|
||||
|
@ -124,6 +114,7 @@ class FieldList extends PureComponent<Props, State> {
|
|||
|
||||
const hasAggregates = numFunctions(fields) > 0
|
||||
const noDBorMeas = !database || !measurement
|
||||
const isDisabled = !isKapacitorRule && !isQuerySupportedByExplorer
|
||||
|
||||
return (
|
||||
<div className="query-builder--column">
|
||||
|
@ -138,7 +129,7 @@ class FieldList extends PureComponent<Props, State> {
|
|||
isKapacitorRule={isKapacitorRule}
|
||||
onTimeShift={this.handleTimeShift}
|
||||
onGroupByTime={this.handleGroupByTime}
|
||||
isDisabled={!isQuerySupportedByExplorer}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
|
@ -174,7 +165,7 @@ class FieldList extends PureComponent<Props, State> {
|
|||
fieldFuncs={fieldFuncs}
|
||||
funcs={functionNames(funcs)}
|
||||
isKapacitorRule={isKapacitorRule}
|
||||
isDisabled={!isQuerySupportedByExplorer}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
|
@ -203,7 +194,9 @@ class FieldList extends PureComponent<Props, State> {
|
|||
isQuerySupportedByExplorer,
|
||||
} = this.props
|
||||
const {fields, groupBy} = query
|
||||
if (!isQuerySupportedByExplorer) {
|
||||
const isDisabled = !isKapacitorRule && !isQuerySupportedByExplorer
|
||||
|
||||
if (isDisabled) {
|
||||
return
|
||||
}
|
||||
const initialGroupBy = {...groupBy, time}
|
||||
|
|
|
@ -6,7 +6,7 @@ import _ from 'lodash'
|
|||
import {showMeasurements} from 'src/shared/apis/metaQuery'
|
||||
import showMeasurementsParser from 'src/shared/parsing/showMeasurements'
|
||||
|
||||
import {QueryConfig, Source} from 'src/types'
|
||||
import {QueryConfig, Source, Tag} from 'src/types'
|
||||
|
||||
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||
import MeasurementListFilter from 'src/shared/components/MeasurementListFilter'
|
||||
|
@ -15,12 +15,13 @@ import {ErrorHandling} from 'src/shared/decorators/errors'
|
|||
|
||||
interface Props {
|
||||
query: QueryConfig
|
||||
querySource: Source
|
||||
onChooseTag: () => void
|
||||
onGroupByTag: () => void
|
||||
onToggleTagAcceptance: () => void
|
||||
isQuerySupportedByExplorer: boolean
|
||||
querySource?: Source
|
||||
onChooseMeasurement: (measurement: string) => void
|
||||
onChooseTag: (tag: Tag) => void
|
||||
onGroupByTag: (tagKey: string) => void
|
||||
onToggleTagAcceptance: () => void
|
||||
isKapacitorRule?: boolean
|
||||
isQuerySupportedByExplorer?: boolean
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -124,6 +125,7 @@ class MeasurementList extends PureComponent<Props, State> {
|
|||
onChooseTag,
|
||||
onGroupByTag,
|
||||
isQuerySupportedByExplorer,
|
||||
isKapacitorRule,
|
||||
} = this.props
|
||||
const {database, areTagsAccepted} = query
|
||||
const {filtered} = this.state
|
||||
|
@ -154,7 +156,9 @@ class MeasurementList extends PureComponent<Props, State> {
|
|||
areTagsAccepted={areTagsAccepted}
|
||||
onAcceptReject={this.handleAcceptReject}
|
||||
isActive={measurement === query.measurement}
|
||||
isQuerySupportedByExplorer={isQuerySupportedByExplorer}
|
||||
isQuerySupportedByExplorer={
|
||||
isKapacitorRule || isQuerySupportedByExplorer
|
||||
}
|
||||
numTagsActive={Object.keys(query.tags).length}
|
||||
onChooseMeasurement={this.handleChoosemeasurement}
|
||||
/>
|
||||
|
|
|
@ -4,6 +4,8 @@ import React, {PureComponent, MouseEvent} from 'react'
|
|||
import TagList from 'src/shared/components/TagList'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
import {QueryConfig, Tag} from 'src/types'
|
||||
|
||||
interface SourceLinks {
|
||||
proxy: string
|
||||
}
|
||||
|
@ -12,31 +14,15 @@ interface Source {
|
|||
links: SourceLinks
|
||||
}
|
||||
|
||||
interface GroupBy {
|
||||
tags?: string[]
|
||||
}
|
||||
|
||||
interface Tags {
|
||||
[key: string]: string[]
|
||||
}
|
||||
|
||||
interface Query {
|
||||
database: string
|
||||
measurement: string
|
||||
retentionPolicy: string
|
||||
tags: Tags
|
||||
groupBy: GroupBy
|
||||
}
|
||||
|
||||
interface Props {
|
||||
query: Query
|
||||
query: QueryConfig
|
||||
querySource: Source
|
||||
isActive: boolean
|
||||
measurement: string
|
||||
numTagsActive: number
|
||||
areTagsAccepted: boolean
|
||||
onChooseTag: () => void
|
||||
onGroupByTag: () => void
|
||||
onChooseTag: (tag: Tag) => void
|
||||
onGroupByTag: (tagKey: string) => void
|
||||
onAcceptReject: () => void
|
||||
isQuerySupportedByExplorer: boolean
|
||||
onChooseMeasurement: (measurement: string) => () => void
|
||||
|
|
|
@ -10,6 +10,8 @@ import showTagKeysParser from 'src/shared/parsing/showTagKeys'
|
|||
import showTagValuesParser from 'src/shared/parsing/showTagValues'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
import {QueryConfig, Tag} from 'src/types'
|
||||
|
||||
const {shape} = PropTypes
|
||||
|
||||
interface SourceLinks {
|
||||
|
@ -20,26 +22,11 @@ interface Source {
|
|||
links: SourceLinks
|
||||
}
|
||||
|
||||
interface GroupBy {
|
||||
tags?: string[]
|
||||
}
|
||||
|
||||
interface Tags {
|
||||
[key: string]: string[]
|
||||
}
|
||||
interface Query {
|
||||
database: string
|
||||
measurement: string
|
||||
retentionPolicy: string
|
||||
tags: Tags
|
||||
groupBy: GroupBy
|
||||
}
|
||||
|
||||
interface Props {
|
||||
query: Query
|
||||
query: QueryConfig
|
||||
querySource: Source
|
||||
onChooseTag: () => void
|
||||
onGroupByTag: () => void
|
||||
onChooseTag: (tag: Tag) => void
|
||||
onGroupByTag: (tagKey: string) => void
|
||||
isQuerySupportedByExplorer: boolean
|
||||
}
|
||||
|
||||
|
|
|
@ -529,6 +529,11 @@ export const notifyTestAlertFailed = (endpoint, errorMessage) => ({
|
|||
}`,
|
||||
})
|
||||
|
||||
export const notifyInvalidBatchSizeValue = () => ({
|
||||
...defaultErrorNotification,
|
||||
message: 'Batch Size cannot be empty.',
|
||||
})
|
||||
|
||||
export const notifyKapacitorConnectionFailed = () => ({
|
||||
...defaultErrorNotification,
|
||||
message:
|
||||
|
|
|
@ -26,6 +26,11 @@ export const numFunctions = fields => _.size(getFunctions(fields))
|
|||
// functionNames returns the value of all top-level functions
|
||||
export const functionNames = fields => getFunctions(fields).map(f => f.value)
|
||||
|
||||
/**
|
||||
* getAllFields and funcs with fieldName
|
||||
* @param {Field[]} fields
|
||||
* @returns {Field[]}
|
||||
*/
|
||||
// returns a flattened list of all fieldNames in a queryConfig
|
||||
export const getFieldsDeep = fields =>
|
||||
_.uniqBy(
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
import * as kapacitorQueryConfigActions from 'src/kapacitor/actions/queryConfigs'
|
||||
type KapacitorQueryConfigActions = typeof kapacitorQueryConfigActions
|
||||
|
||||
export {KapacitorQueryConfigActions}
|
|
@ -76,21 +76,3 @@ export interface Template {
|
|||
tempVar: string
|
||||
values: TemplateValue[]
|
||||
}
|
||||
|
||||
export type CellEditorOverlayActionsFunc = (id: string, ...args: any[]) => any
|
||||
|
||||
export interface CellEditorOverlayActions {
|
||||
chooseNamespace: (id: string) => void
|
||||
chooseMeasurement: (id: string) => void
|
||||
applyFuncsToField: (id: string) => void
|
||||
chooseTag: (id: string) => void
|
||||
groupByTag: (id: string) => void
|
||||
toggleField: (id: string) => void
|
||||
groupByTime: (id: string) => void
|
||||
toggleTagAcceptance: (id: string) => void
|
||||
fill: (id: string) => void
|
||||
editRawTextAsync: (url: string, id: string, text: string) => Promise<void>
|
||||
addInitialField: (id: string) => void
|
||||
removeFuncs: (id: string) => void
|
||||
timeShift: (id: string) => void
|
||||
}
|
||||
|
|
|
@ -6,12 +6,19 @@ import {
|
|||
Status,
|
||||
TimeRange,
|
||||
TimeShift,
|
||||
ApplyFuncsToFieldArgs,
|
||||
Field,
|
||||
FieldFunc,
|
||||
FuncArg,
|
||||
Namespace,
|
||||
Tag,
|
||||
Tags,
|
||||
TagValues,
|
||||
} from './query'
|
||||
import {AlertRule, Kapacitor, Task} from './kapacitor'
|
||||
import {Source, SourceLinks} from './sources'
|
||||
import {DropdownAction, DropdownItem} from './shared'
|
||||
import {Notification} from 'src/kapacitor/components/AlertOutputs'
|
||||
import {Notification, NotificationFunc} from './notifications'
|
||||
|
||||
export {
|
||||
Me,
|
||||
|
@ -26,8 +33,15 @@ export {
|
|||
Status,
|
||||
QueryConfig,
|
||||
TimeShift,
|
||||
ApplyFuncsToFieldArgs,
|
||||
Field,
|
||||
FieldFunc,
|
||||
FuncArg,
|
||||
GroupBy,
|
||||
Namespace,
|
||||
Tag,
|
||||
Tags,
|
||||
TagValues,
|
||||
AlertRule,
|
||||
Kapacitor,
|
||||
Source,
|
||||
|
@ -37,4 +51,5 @@ export {
|
|||
TimeRange,
|
||||
Task,
|
||||
Notification,
|
||||
NotificationFunc,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
export interface Notification {
|
||||
id?: string
|
||||
type: string
|
||||
icon: string
|
||||
duration: number
|
||||
message: string
|
||||
}
|
||||
|
||||
export type NotificationFunc = () => Notification
|
|
@ -20,14 +20,33 @@ export interface Field {
|
|||
value: string
|
||||
type: string
|
||||
alias?: string
|
||||
args?: Args[]
|
||||
args?: FieldArg[]
|
||||
}
|
||||
|
||||
export interface Args {
|
||||
export interface FieldArg {
|
||||
value: string
|
||||
type: string
|
||||
alias?: string
|
||||
args?: Args[]
|
||||
args?: FieldArg[]
|
||||
}
|
||||
|
||||
export interface FieldFunc extends Field {
|
||||
args: FuncArg[]
|
||||
}
|
||||
export interface FuncArg {
|
||||
type: string
|
||||
value: string
|
||||
alias?: string
|
||||
}
|
||||
|
||||
export interface ApplyFuncsToFieldArgs {
|
||||
field: Field
|
||||
funcs: FuncArg[]
|
||||
}
|
||||
|
||||
export interface Tag {
|
||||
key: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export type TagValues = string[]
|
||||
|
@ -37,7 +56,7 @@ export interface Tags {
|
|||
}
|
||||
|
||||
export interface GroupBy {
|
||||
time?: string
|
||||
time?: string | null
|
||||
tags?: string[]
|
||||
}
|
||||
|
||||
|
|
|
@ -1,33 +1,44 @@
|
|||
import _ from 'lodash'
|
||||
import defaultQueryConfig from 'utils/defaultQueryConfig'
|
||||
import defaultQueryConfig from 'src/utils/defaultQueryConfig'
|
||||
import {
|
||||
hasField,
|
||||
removeField,
|
||||
getFieldsDeep,
|
||||
getFuncsByFieldName,
|
||||
} from 'shared/reducers/helpers/fields'
|
||||
} from 'src/shared/reducers/helpers/fields'
|
||||
|
||||
export function editRawText(query, rawText) {
|
||||
return Object.assign({}, query, {rawText})
|
||||
}
|
||||
import {
|
||||
Field,
|
||||
GroupBy,
|
||||
Namespace,
|
||||
QueryConfig,
|
||||
Tag,
|
||||
TagValues,
|
||||
TimeShift,
|
||||
ApplyFuncsToFieldArgs,
|
||||
} from 'src/types'
|
||||
|
||||
export const chooseNamespace = (query, namespace, isKapacitorRule = false) => ({
|
||||
export const chooseNamespace = (
|
||||
query: QueryConfig,
|
||||
namespace: Namespace,
|
||||
isKapacitorRule: boolean = false
|
||||
): QueryConfig => ({
|
||||
...defaultQueryConfig({id: query.id, isKapacitorRule}),
|
||||
...namespace,
|
||||
})
|
||||
|
||||
export const chooseMeasurement = (
|
||||
query,
|
||||
measurement,
|
||||
isKapacitorRule = false
|
||||
) => ({
|
||||
query: QueryConfig,
|
||||
measurement: string,
|
||||
isKapacitorRule: boolean = false
|
||||
): QueryConfig => ({
|
||||
...defaultQueryConfig({id: query.id, isKapacitorRule}),
|
||||
database: query.database,
|
||||
retentionPolicy: query.retentionPolicy,
|
||||
measurement,
|
||||
})
|
||||
|
||||
export const toggleKapaField = (query, field) => {
|
||||
export const toggleKapaField = (query: QueryConfig, field: Field) => {
|
||||
if (field.type === 'field') {
|
||||
return {
|
||||
...query,
|
||||
|
@ -45,24 +56,29 @@ export const toggleKapaField = (query, field) => {
|
|||
}
|
||||
}
|
||||
|
||||
export const buildInitialField = value => [
|
||||
{
|
||||
type: 'func',
|
||||
alias: `mean_${value}`,
|
||||
args: [{value, type: 'field'}],
|
||||
value: 'mean',
|
||||
},
|
||||
]
|
||||
export const buildInitialField = (value: string): Field => ({
|
||||
type: 'func',
|
||||
alias: `mean_${value}`,
|
||||
args: [{value, type: 'field'}],
|
||||
value: 'mean',
|
||||
})
|
||||
|
||||
export const addInitialField = (query, field, groupBy) => {
|
||||
export const addInitialField = (
|
||||
query: QueryConfig,
|
||||
field,
|
||||
groupBy
|
||||
): QueryConfig => {
|
||||
return {
|
||||
...query,
|
||||
fields: buildInitialField(field.value),
|
||||
fields: [buildInitialField(field.value)],
|
||||
groupBy,
|
||||
}
|
||||
}
|
||||
|
||||
export const toggleField = (query, {value}) => {
|
||||
export const toggleField = (
|
||||
query: QueryConfig,
|
||||
{value}: Field
|
||||
): QueryConfig => {
|
||||
const {fields = [], groupBy} = query
|
||||
const isSelected = hasField(value, fields)
|
||||
const newFuncs = fields.filter(f => f.type === 'func')
|
||||
|
@ -108,29 +124,35 @@ export const toggleField = (query, {value}) => {
|
|||
}
|
||||
}
|
||||
|
||||
export const groupByTime = (query, time) => {
|
||||
return Object.assign({}, query, {
|
||||
groupBy: Object.assign({}, query.groupBy, {
|
||||
time,
|
||||
}),
|
||||
})
|
||||
}
|
||||
export const groupByTime = (query: QueryConfig, time: string): QueryConfig => ({
|
||||
...query,
|
||||
groupBy: {...query.groupBy, time},
|
||||
})
|
||||
|
||||
export const fill = (query, value) => ({...query, fill: value})
|
||||
export const fill = (query: QueryConfig, value: string): QueryConfig => ({
|
||||
...query,
|
||||
fill: value,
|
||||
})
|
||||
|
||||
export const toggleTagAcceptance = query => {
|
||||
return Object.assign({}, query, {
|
||||
areTagsAccepted: !query.areTagsAccepted,
|
||||
})
|
||||
}
|
||||
export const toggleTagAcceptance = (query: QueryConfig): QueryConfig => ({
|
||||
...query,
|
||||
areTagsAccepted: !query.areTagsAccepted,
|
||||
})
|
||||
|
||||
export const removeFuncs = (query, fields) => ({
|
||||
export const removeFuncs = (
|
||||
query: QueryConfig,
|
||||
fields: Field[]
|
||||
): QueryConfig => ({
|
||||
...query,
|
||||
fields: getFieldsDeep(fields),
|
||||
groupBy: {...query.groupBy, time: null},
|
||||
})
|
||||
|
||||
export const applyFuncsToField = (query, {field, funcs = []}, groupBy) => {
|
||||
export const applyFuncsToField = (
|
||||
query: QueryConfig,
|
||||
{field, funcs = []}: ApplyFuncsToFieldArgs,
|
||||
groupBy: GroupBy
|
||||
): QueryConfig => {
|
||||
const nextFields = query.fields.reduce((acc, f) => {
|
||||
// If there is a func applied to only one field, add it to the other fields
|
||||
if (f.type === 'field') {
|
||||
|
@ -185,13 +207,12 @@ export const applyFuncsToField = (query, {field, funcs = []}, groupBy) => {
|
|||
}
|
||||
}
|
||||
|
||||
export const updateRawQuery = (query, rawText) => {
|
||||
return Object.assign({}, query, {
|
||||
rawText,
|
||||
})
|
||||
}
|
||||
export const updateRawQuery = (query: QueryConfig, rawText): QueryConfig => ({
|
||||
...query,
|
||||
rawText,
|
||||
})
|
||||
|
||||
export const groupByTag = (query, tagKey) => {
|
||||
export const groupByTag = (query: QueryConfig, tagKey: string): QueryConfig => {
|
||||
const oldTags = query.groupBy.tags
|
||||
let newTags
|
||||
|
||||
|
@ -204,27 +225,30 @@ export const groupByTag = (query, tagKey) => {
|
|||
newTags = oldTags.concat(tagKey)
|
||||
}
|
||||
|
||||
return Object.assign({}, query, {
|
||||
groupBy: Object.assign({}, query.groupBy, {tags: newTags}),
|
||||
})
|
||||
return {
|
||||
...query,
|
||||
groupBy: {...query.groupBy, tags: newTags},
|
||||
}
|
||||
}
|
||||
|
||||
export const chooseTag = (query, tag) => {
|
||||
export const chooseTag = (query: QueryConfig, tag: Tag): QueryConfig => {
|
||||
const tagValues = query.tags[tag.key]
|
||||
const shouldRemoveTag =
|
||||
tagValues && tagValues.length === 1 && tagValues[0] === tag.value
|
||||
if (shouldRemoveTag) {
|
||||
const newTags = Object.assign({}, query.tags)
|
||||
const newTags = {...query.tags}
|
||||
delete newTags[tag.key]
|
||||
return Object.assign({}, query, {tags: newTags})
|
||||
return {...query, tags: newTags}
|
||||
}
|
||||
|
||||
const updateTagValues = newTagValues => {
|
||||
return Object.assign({}, query, {
|
||||
tags: Object.assign({}, query.tags, {
|
||||
const updateTagValues = (newTagValues: TagValues): QueryConfig => {
|
||||
return {
|
||||
...query,
|
||||
tags: {
|
||||
...query.tags,
|
||||
[tag.key]: newTagValues,
|
||||
}),
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const oldTagValues = query.tags[tag.key]
|
||||
|
@ -243,4 +267,7 @@ export const chooseTag = (query, tag) => {
|
|||
return updateTagValues(query.tags[tag.key].concat(tag.value))
|
||||
}
|
||||
|
||||
export const timeShift = (query, shift) => ({...query, shifts: [shift]})
|
||||
export const timeShift = (query: QueryConfig, shift: TimeShift) => ({
|
||||
...query,
|
||||
shifts: [shift],
|
||||
})
|
|
@ -3,13 +3,7 @@ import React from 'react'
|
|||
import MeasurementListItem from 'src/shared/components/MeasurementListItem'
|
||||
import TagList from 'src/shared/components/TagList'
|
||||
|
||||
const defaultQuery = {
|
||||
database: '',
|
||||
measurement: 'test',
|
||||
retentionPolicy: '',
|
||||
tags: {},
|
||||
groupBy: {},
|
||||
}
|
||||
import {query as defaultQuery} from 'test/resources'
|
||||
|
||||
const setup = (overrides = {}) => {
|
||||
const props = {
|
||||
|
@ -20,7 +14,7 @@ const setup = (overrides = {}) => {
|
|||
},
|
||||
},
|
||||
isActive: true,
|
||||
measurement: 'test',
|
||||
measurement: defaultQuery.measurement,
|
||||
numTagsActive: 3,
|
||||
areTagsAccepted: true,
|
||||
isQuerySupportedByExplorer: true,
|
||||
|
@ -75,7 +69,7 @@ describe('MeasurementListItem', () => {
|
|||
|
||||
wrapper.simulate('click')
|
||||
wrapper.setProps({query: {...defaultQuery, measurement}})
|
||||
expect(factory).toHaveBeenCalledWith('test')
|
||||
expect(factory).toHaveBeenCalledWith(defaultQuery.measurement)
|
||||
expect(trigger).toHaveBeenCalled()
|
||||
|
||||
expect(wrapper.find(TagList).exists()).toBe(true)
|
||||
|
|
Loading…
Reference in New Issue