Add http endpoint (#14845)

* Add http endpoint

* Add http notification rule as option

* Update test
pull/14855/head
Deniz Kusefoglu 2019-08-28 13:06:19 -07:00 committed by GitHub
parent 2fa1ca3f49
commit 456dfcf4d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 244 additions and 23 deletions

View File

@ -50,7 +50,7 @@ describe('Notification Endpoints', () => {
.click()
.within(() => {
cy.getByTestID('endpoint--dropdown--button').within(() => {
cy.contains('Slack')
cy.contains('HTTP')
})
cy.getByTestID('endpoint--dropdown-item pagerduty').click()

View File

@ -0,0 +1,72 @@
// Libraries
import React, {FC} from 'react'
// Components
import {Dropdown} from '@influxdata/clockface'
// Types
import {HTTPAuthMethodType} from 'src/types'
interface AuthMethodType {
name: string
type: HTTPAuthMethodType
id: HTTPAuthMethodType
}
interface Props {
selectedType: string
onSelectType: (type: HTTPAuthMethodType) => void
}
const types: AuthMethodType[] = [
{name: 'none', type: 'none', id: 'none'},
{name: 'basic', type: 'basic', id: 'basic'},
{name: 'bearer', type: 'bearer', id: 'bearer'},
]
const AuthMethodTypeDropdown: FC<Props> = ({selectedType, onSelectType}) => {
const items = types.map(({id, type, name}) => (
<Dropdown.Item
key={id}
id={id}
value={id}
testID={`http-authMethod--dropdown-item ${type}`}
onClick={onSelectType}
>
{name}
</Dropdown.Item>
))
const selected = types.find(t => t.type === selectedType)
if (!selected) {
throw new Error(
'Incorrect authMethod type provided to <AuthMethodTypeDropdown/>'
)
}
const button = (active, onClick) => (
<Dropdown.Button
testID="http-authMethod--dropdown--button"
active={active}
onClick={onClick}
>
{selected.name}
</Dropdown.Button>
)
const menu = onCollapse => (
<Dropdown.Menu onCollapse={onCollapse}>{items}</Dropdown.Menu>
)
return (
<Dropdown
button={button}
menu={menu}
widthPixels={160}
testID="http-authMethod-change--dropdown"
/>
)
}
export default AuthMethodTypeDropdown

View File

@ -16,10 +16,15 @@ import {
interface Props {
endpoint: NotificationEndpoint
onChange: (e: ChangeEvent<HTMLInputElement>) => void
onChange: (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void
onChangeParameter: (key: string) => (value: string) => void
}
const EndpointOptions: FC<Props> = ({endpoint, onChange}) => {
const EndpointOptions: FC<Props> = ({
endpoint,
onChange,
onChangeParameter,
}) => {
switch (endpoint.type) {
case 'slack': {
const {url, token} = endpoint as SlackNotificationEndpoint
@ -38,8 +43,6 @@ const EndpointOptions: FC<Props> = ({endpoint, onChange}) => {
)
}
case 'http': {
// TODO(watts): add webhook type to the `Destination` dropdown
// when webhooks are implemented in the backend.
const {
url,
token,
@ -51,6 +54,8 @@ const EndpointOptions: FC<Props> = ({endpoint, onChange}) => {
} = endpoint as HTTPNotificationEndpoint
return (
<EndpointOptionsHTTP
onChange={onChange}
onChangeParameter={onChangeParameter}
url={url}
token={token}
username={username}

View File

@ -1,8 +1,10 @@
// Libraries
import React, {FC} from 'react'
import React, {FC, ChangeEvent} from 'react'
// Components
import {Input, FormElement, InputType} from '@influxdata/clockface'
import {Input, FormElement, InputType, TextArea} from '@influxdata/clockface'
import MethodTypeDropdown from 'src/alerting/components/endpoints/MethodTypeDropdown'
import AuthMethodTypeDropdown from 'src/alerting/components/endpoints/AuthMethodTypeDropdown'
// Types
import {HTTPNotificationEndpoint} from 'src/types'
@ -15,32 +17,80 @@ interface Props {
method?: HTTPNotificationEndpoint['method']
authMethod?: HTTPNotificationEndpoint['authMethod']
contentTemplate: string
onChange: (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void
onChangeParameter: (key: string) => (value: string) => void
}
const EndpointOptionsHTTP: FC<Props> = ({
url,
onChange,
token,
username,
password,
contentTemplate,
method,
authMethod,
onChangeParameter,
}) => {
return (
<>
<FormElement label="URL">
<Input name="url" value={url} />
<Input name="url" value={url} onChange={onChange} required={true} />
</FormElement>
<FormElement label="Token">
<Input name="token" value={token} />
<FormElement label="HTTP method">
<MethodTypeDropdown
onSelectType={onChangeParameter('method')}
selectedType={method}
/>
</FormElement>
<FormElement label="username">
<Input name="username" value={username} />
<FormElement label="auth method">
<AuthMethodTypeDropdown
onSelectType={onChangeParameter('authMethod')}
selectedType={authMethod}
/>
</FormElement>
<FormElement label="password">
<Input name="password" value={password} type={InputType.Password} />
</FormElement>
{/** add dropdowns for method and authmethod */}
{authMethod === 'bearer' && (
<FormElement label="Token">
<Input
name="token"
value={token}
onChange={onChange}
type={InputType.Password}
/>
</FormElement>
)}
{authMethod === 'basic' && (
<>
<FormElement label="username">
<Input
name="username"
value={username}
onChange={onChange}
type={
username && username.includes('secret: ')
? InputType.Password
: InputType.Text
}
/>
</FormElement>
<FormElement label="password">
<Input
name="password"
value={password}
type={InputType.Password}
onChange={onChange}
/>
</FormElement>
</>
)}
<FormElement label="Content Template">
<Input name="contentTemplate" value={contentTemplate} />
<TextArea
rows={2}
className="endpoint-description--textarea"
name="contentTemplate"
value={contentTemplate}
onChange={onChange}
/>
</FormElement>
</>
)

View File

@ -30,6 +30,13 @@ const EndpointOverlayContents: FC<Props> = ({onSave, saveButtonText}) => {
})
}
const handleChangeParameter = (key: string) => (value: string) => {
dispatch({
type: 'UPDATE_ENDPOINT',
endpoint: {...endpoint, [key]: value},
})
}
const handleSelectType = (type: NotificationEndpointType) => {
dispatch({
type: 'UPDATE_ENDPOINT',
@ -55,11 +62,10 @@ const EndpointOverlayContents: FC<Props> = ({onSave, saveButtonText}) => {
</Form.Element>
<Form.Element label="Description">
<TextArea
rows={5}
rows={1}
className="endpoint-description--textarea"
testID="endpoint-description--textarea"
name="description"
placeholder="Optional"
value={endpoint.description}
onChange={handleChange}
/>
@ -70,7 +76,11 @@ const EndpointOverlayContents: FC<Props> = ({onSave, saveButtonText}) => {
selectedType={endpoint.type}
/>
</Form.Element>
<EndpointOptions endpoint={endpoint} onChange={handleChange} />
<EndpointOptions
endpoint={endpoint}
onChange={handleChange}
onChangeParameter={handleChangeParameter}
/>
<EndpointOverlayFooter
onSave={onSave}
saveButtonText={saveButtonText}

View File

@ -19,6 +19,7 @@ interface Props {
}
const types: EndpointType[] = [
{name: 'HTTP', type: 'http', id: 'http'},
{name: 'Slack', type: 'slack', id: 'slack'},
{name: 'Pagerduty', type: 'pagerduty', id: 'pagerduty'},
]

View File

@ -0,0 +1,70 @@
// Libraries
import React, {FC} from 'react'
// Components
import {Dropdown} from '@influxdata/clockface'
// Types
import {HTTPMethodType} from 'src/types'
interface MethodType {
name: string
type: HTTPMethodType
id: HTTPMethodType
}
interface Props {
selectedType: string
onSelectType: (type: HTTPMethodType) => void
}
const types: MethodType[] = [
{name: 'POST', type: 'POST', id: 'POST'},
{name: 'GET', type: 'GET', id: 'GET'},
{name: 'PUT', type: 'PUT', id: 'PUT'},
]
const MethodTypeDropdown: FC<Props> = ({selectedType, onSelectType}) => {
const items = types.map(({id, type, name}) => (
<Dropdown.Item
key={id}
id={id}
value={id}
testID={`http-method--dropdown-item ${type}`}
onClick={onSelectType}
>
{name}
</Dropdown.Item>
))
const selected = types.find(t => t.type === selectedType)
if (!selected) {
throw new Error('Incorrect method type provided to <MethodTypeDropdown/>')
}
const button = (active, onClick) => (
<Dropdown.Button
testID="http-method--dropdown--button"
active={active}
onClick={onClick}
>
{selected.name}
</Dropdown.Button>
)
const menu = onCollapse => (
<Dropdown.Menu onCollapse={onCollapse}>{items}</Dropdown.Menu>
)
return (
<Dropdown
button={button}
menu={menu}
widthPixels={160}
testID="http-method-change--dropdown"
/>
)
}
export default MethodTypeDropdown

View File

@ -15,12 +15,14 @@ import {
NotificationRule,
NotificationRuleDraft,
NewNotificationRule,
HTTPNotificationRuleBase,
} from 'src/types'
type RuleVariantFields =
| SlackNotificationRuleBase
| SMTPNotificationRuleBase
| PagerDutyNotificationRuleBase
| HTTPNotificationRuleBase
export const getRuleVariantDefaults = (
endpoints: NotificationEndpoint[],
@ -37,6 +39,10 @@ export const getRuleVariantDefaults = (
return {messageTemplate: '', type: 'pagerduty'}
}
case 'http': {
return {type: 'http', url: ''}
}
default: {
throw new Error(`Could not find NotificationEndpoint with id "${id}"`)
}

View File

@ -112,10 +112,12 @@ export const NEW_TAG_RULE_DRAFT: TagRuleDraft = {
}
export const NEW_ENDPOINT_DRAFT: NotificationEndpoint = {
name: 'HTTP Endpoint',
method: 'POST',
authMethod: 'none',
description: '',
name: 'Slack Endpoint',
status: 'active',
type: 'slack',
type: 'http',
token: '',
url: '',
}

View File

@ -89,16 +89,18 @@ export {
SMTPNotificationRuleBase,
SlackNotificationRuleBase,
PagerDutyNotificationRuleBase,
HTTPNotificationRuleBase,
SMTPNotificationRule,
SlackNotificationRule,
PagerDutyNotificationRule,
HTTPNotificationRule,
PagerDutyNotificationEndpoint,
SlackNotificationEndpoint,
HTTPNotificationEndpoint,
NotificationEndpointUpdate,
} from '../client'
import {Check, Threshold} from '../client'
import {Check, Threshold, HTTPNotificationEndpoint} from '../client'
export type CheckType = Check['type']
export type ThresholdType = Threshold['type']
@ -106,3 +108,6 @@ export type ThresholdType = Threshold['type']
export type CheckTagSet = Check['tags'][0]
export type AlertHistoryType = 'statuses' | 'notifications'
export type HTTPMethodType = HTTPNotificationEndpoint['method']
export type HTTPAuthMethodType = HTTPNotificationEndpoint['authMethod']