Skip to content

Commit

Permalink
added support for webhook action
Browse files Browse the repository at this point in the history
  • Loading branch information
alisonelizabeth committed Apr 19, 2019
1 parent ab7f44e commit ac0b569
Show file tree
Hide file tree
Showing 10 changed files with 530 additions and 103 deletions.
2 changes: 1 addition & 1 deletion x-pack/plugins/watcher/public/models/action/action.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { ACTION_TYPES } from 'plugins/watcher/../common/constants';
import { EmailAction } from './email_action';
import { LoggingAction } from './logging_action';
import { SlackAction } from './slack_action';
import { WebhookAction } from './webhook.action';
import { WebhookAction } from './webhook_action';
import { IndexAction } from './index_action';
import { PagerDutyAction } from './pagerduty.action';
import { JiraAction } from './jira.action';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,4 @@ export class LoggingAction extends BaseAction {
static simulatePrompt = i18n.translate('xpack.watcher.models.loggingAction.simulateButtonLabel', {
defaultMessage: 'Log a sample message now',
});

}
98 changes: 0 additions & 98 deletions x-pack/plugins/watcher/public/models/action/webhook.action.js

This file was deleted.

126 changes: 126 additions & 0 deletions x-pack/plugins/watcher/public/models/action/webhook_action.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/


import { get } from 'lodash';
import { BaseAction } from './base_action';
import { i18n } from '@kbn/i18n';

export class WebhookAction extends BaseAction {
constructor(props = {}) {
super(props);

this.method = get(props, 'method');
this.host = get(props, 'host');
this.port = get(props, 'port');
this.path = get(props, 'path');
this.body = get(props, 'body');
this.fullPath = `${this.host}${this.port}${this.path}`;
}


validateAction() {
const errors = {
host: [],
port: [],
body: [],
};

if (!this.host) {
errors.host.push(
i18n.translate('xpack.watcher.watchActions.webhook.hostIsRequiredValidationMessage', {
defaultMessage: 'Host is required.',
})
);
}
if (!this.port) {
errors.port.push(
i18n.translate('xpack.watcher.watchActions.webhook.portIsRequiredValidationMessage', {
defaultMessage: 'Port is required.',
})
);
}
if (this.body || this.body !== '') {
try {
const parsedJson = JSON.parse(this.body);
if (parsedJson && typeof parsedJson !== 'object') {
errors.body.push(i18n.translate('xpack.watcher.watchActions.webhook.bodyParseValidationMessage', {
defaultMessage: 'Invalid JSON',
}));
}
} catch (e) {
errors.body.push(i18n.translate('xpack.watcher.watchActions.webhook.bodyParseValidationMessage', {
defaultMessage: 'Invalid JSON',
}));
}
}
return errors;
}

get upstreamJson() {
const result = super.upstreamJson;

Object.assign(result, {
method: this.method,
host: this.host,
port: this.port,
path: this.path,
body: this.body,
webhook: {
host: this.host,
port: this.port,
}
});

return result;
}

get description() {
return i18n.translate('xpack.watcher.models.webhookAction.description', {
defaultMessage: 'Webhook will trigger a {method} request on {fullPath}',
values: {
method: this.method,
fullPath: this.fullPath
}
});
}

get simulateMessage() {
return i18n.translate('xpack.watcher.models.webhookAction.simulateMessage', {
defaultMessage: 'Sample request sent to {fullPath}',
values: {
fullPath: this.fullPath
}
});
}

get simulateFailMessage() {
return i18n.translate('xpack.watcher.models.webhookAction.simulateFailMessage', {
defaultMessage: 'Failed to send request to {fullPath}.',
values: {
fullPath: this.fullPath
}
});
}

static fromUpstreamJson(upstreamAction) {
return new WebhookAction(upstreamAction);
}

static defaults = {
body: JSON.stringify({ message: 'Watch [{{ctx.metadata.name}}] has exceeded the threshold' }, null, 2)
};
static typeName = i18n.translate('xpack.watcher.models.webhookAction.typeName', {
defaultMessage: 'Webhook',
});
static iconClass = 'logoWebhook';
static selectMessage = i18n.translate('xpack.watcher.models.webhookAction.selectMessageText', {
defaultMessage: 'Send a request to any web service.',
});
static simulatePrompt = i18n.translate('xpack.watcher.models.webhookAction.simulateButtonLabel', {
defaultMessage: 'Send request now',
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { Fragment } from 'react';
import { EuiFieldNumber, EuiFieldText, EuiTextArea } from '@elastic/eui';
import { ErrableFormRow } from '../../../components/form_errors';

interface Props {
action: any; // TODO fix
// fields: Array<{
// required: boolean;
// fieldType: 'text' | 'textarea' | 'array' | 'number';
// fieldName: string;
// fieldLabel: string;
// }>;
editAction: (changedProperty: { key: string; value: string }) => void;
}

const FORM_CONTROLS = {
text: EuiFieldText,
textarea: EuiTextArea,
array: EuiFieldText, // TODO replace with combo box?
number: EuiFieldNumber,
};

export const ThresholdActionsFormBuilder: React.FunctionComponent<Props> = ({
action,
editAction,
}) => {
const errors = action.validateAction();
const hasErrors = !!Object.keys(errors).find(errorKey => errors[errorKey].length >= 1);
return (
<Fragment>
{action.fields.map((field, index) => {
const { required, fieldName, fieldLabel, fieldType } = field;
const fieldValue = action[fieldName];
if (required) {
const FormControl = FORM_CONTROLS[fieldType];
const currentValue = Array.isArray(fieldValue) ? fieldValue.join(', ') : fieldValue; // TODO cleanup/rename?
return (
<ErrableFormRow
key={`${action.typeName}-${fieldName}-${index}`}
id={`${action.typeName}-${fieldName}-${index}`}
errorKey={fieldName}
fullWidth
errors={errors}
isShowingErrors={hasErrors && fieldValue !== undefined}
label={fieldLabel}
>
<FormControl
fullWidth
name={fieldName}
value={currentValue || ''}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
let newValue = e.target.value;
if (fieldType === 'array') {
const toArray = (newValue || '').split(',').map(val => val.trim());
newValue = toArray.join(', ');
}
if (fieldType === 'number') {
newValue = parseInt(newValue, 10);
}
editAction({ key: fieldName, value: newValue });
}}
onBlur={() => {
if (!action[fieldName]) {
editAction({ key: fieldName, value: fieldType === 'array' ? [] : '' });
}
}}
/>
</ErrableFormRow>
);
}
// TODO implement non-required form row
return null;
})}
</Fragment>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,19 @@ import { WatchHistoryItem } from 'plugins/watcher/models/watch_history_item';
import { WatchAction } from '../../../../common/types/watch_types';
import { ACTION_TYPES, ACTION_MODES } from '../../../../common/constants';
import { WatchContext } from './watch_context';
import { EmailActionFields } from './email_action_fields';
import { WebhookActionFields } from './webhook_action_fields';
import { LoggingActionFields } from './logging_action_fields';
import { SlackActionFields } from './slack_action_fields';
import { IndexActionFields } from './index_action_fields';
import { SlackActionFields } from './slack_action_fields';
import { EmailActionFields } from './email_action_fields';
import { executeWatch } from '../../../lib/api';

const ActionFieldsComponent = {
[ACTION_TYPES.LOGGING]: LoggingActionFields,
[ACTION_TYPES.SLACK]: SlackActionFields,
[ACTION_TYPES.EMAIL]: EmailActionFields,
[ACTION_TYPES.INDEX]: IndexActionFields,
[ACTION_TYPES.WEBHOOK]: WebhookActionFields,
// TODO add support for additional action types
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ export const WatchActionsDropdown: React.FunctionComponent = () => {
typeName,
iconClass,
selectMessage,
isEnabled: settings.actionTypes[actionKey].enabled,
isEnabled: true, // TODO temp
// isEnabled: settings.actionTypes[actionKey].enabled,
};
});
setActions(newActions);
Expand Down
Loading

0 comments on commit ac0b569

Please sign in to comment.