Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Advanced watch cleanup #34955

Merged
3 changes: 1 addition & 2 deletions x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -7965,7 +7965,6 @@
"xpack.watcher.sections.watchEdit.json.simulateTabTitle": "模拟监视",
"xpack.watcher.sections.watchEdit.json.simulateWatchButtonLabel": "模拟监视",
"xpack.watcher.sections.watchEdit.json.simulationOutputLabel": "模拟输出:",
"xpack.watcher.sections.watchEdit.json.warningPossibleInvalidSlackAction.description": "此监视具有不包含“to”属性的 Slack 操作。 只有在 Elasticsearch 的 Slack“message_default”中指定了“to”属性,此监视才有效。",
"xpack.watcher.sections.watchEdit.json.watchErrorsWarning.confirmSaveWatch": "保存监视",
"xpack.watcher.sections.watchEdit.threshold.matchingFollowingConditionTitle": "匹配以下条件",
"xpack.watcher.sections.watchEdit.threshold.saveButtonLabel": "保存",
Expand Down Expand Up @@ -8079,4 +8078,4 @@
"xpack.watcher.watchActionsTitle": "满足后将执行 {watchActionsCount, plural, one{# 个操作} other {# 个操作}}",
"xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。"
}
}
}
1 change: 0 additions & 1 deletion x-pack/plugins/watcher/common/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,3 @@ export { WATCH_HISTORY } from './watch_history';
export { WATCH_STATES } from './watch_states';
export { WATCH_TYPES } from './watch_types';
export { ERROR_CODES } from './error_codes';
export { WATCH_TABS, WATCH_TAB_ID_EDIT, WATCH_TAB_ID_SIMULATE } from './watch_tabs';
30 changes: 0 additions & 30 deletions x-pack/plugins/watcher/common/constants/watch_tabs.ts

This file was deleted.

12 changes: 6 additions & 6 deletions x-pack/plugins/watcher/common/types/watch_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ export interface ExecutedWatchResults {
startTime: Date;
watchStatus: {
state: string;
actionStatuses: Array<{ state: string; lastExecutionReason: string }>;
actionStatuses: Array<{ state: string; lastExecutionReason: string; id: string }>;
};
}

export interface ExecutedWatchDetails {
triggerData: {
triggeredTime: Date;
scheduledTime: Date;
};
scheduledTimeValue: string | undefined;
scheduledTimeUnit: string;
triggeredTimeValue: string | undefined;
triggeredTimeUnit: string;
ignoreCondition: boolean;
alternativeInput: any;
actionModes: {
Expand All @@ -42,7 +42,7 @@ export interface BaseWatch {
upstreamJson: any;
resetActions: () => void;
createAction: (type: string, actionProps: {}) => void;
validate: () => { warning: { message: string } };
validate: () => { warning: { message: string; title?: string } };
actions: [
{
id: string;
Expand Down
28 changes: 18 additions & 10 deletions x-pack/plugins/watcher/public/components/confirm_watches_modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,23 @@ export const ConfirmWatchesModal = ({
modalOptions,
callback,
}: {
modalOptions: { message: string } | null;
modalOptions: {
title: string;
message: string;
buttonLabel?: string;
buttonType?: 'primary' | 'danger';
} | null;
callback: (isConfirmed?: boolean) => void;
}) => {
if (!modalOptions) {
return null;
}
const { title, message, buttonType, buttonLabel } = modalOptions;
return (
<EuiOverlayMask>
<EuiConfirmModal
buttonColor="danger"
title={i18n.translate('xpack.watcher.sections.watchEdit.json.saveConfirmModal.title', {
defaultMessage: 'Confirm save',
})}
buttonColor={buttonType ? buttonType : 'primary'}
title={title}
onCancel={() => callback()}
onConfirm={() => {
callback(true);
Expand All @@ -32,12 +36,16 @@ export const ConfirmWatchesModal = ({
'xpack.watcher.sections.watchEdit.json.saveConfirmModal.cancelButtonLabel',
{ defaultMessage: 'Cancel' }
)}
confirmButtonText={i18n.translate(
'xpack.watcher.sections.watchEdit.json.saveConfirmModal.saveButtonLabel',
{ defaultMessage: 'Save' }
)}
confirmButtonText={
buttonLabel
? buttonLabel
: i18n.translate(
'xpack.watcher.sections.watchEdit.json.saveConfirmModal.saveButtonLabel',
{ defaultMessage: 'Save watch' }
)
}
>
{modalOptions.message}
{message}
</EuiConfirmModal>
</EuiOverlayMask>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,11 @@ export const documentationLinks = {
putWatchApi: makeDocumentationLink(
'{baseUrl}guide/en/elasticsearch/reference/{urlVersion}/watcher-api-put-watch.html'
),
executeWatchApi: makeDocumentationLink(
'{baseUrl}guide/en/elasticsearch/reference/{urlVersion}/watcher-api-execute-watch.html#watcher-api-execute-watch-action-mode'
),
watchNotificationSettings: makeDocumentationLink(
'{baseUrl}guide/en/elasticsearch/reference/{urlVersion}/notification-settings.html#slack-notification-settings'
),
},
};
28 changes: 24 additions & 4 deletions x-pack/plugins/watcher/public/models/action/slack_action.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React from 'react';
import { get, isArray } from 'lodash';
import { BaseAction } from './base_action';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiCode, EuiLink } from '@elastic/eui';
import { documentationLinks } from '../../lib/documentation_links';

export class SlackAction extends BaseAction {
constructor(props = {}) {
Expand All @@ -21,11 +25,27 @@ export class SlackAction extends BaseAction {
const errors = [];

if (!this.to.length) {
const message = (
<FormattedMessage
id="xpack.watcher.sections.watchEdit.json.slackActionValidationWarningMessage"
defaultMessage="This watch has a Slack {ymlValue} setting without a 'to' property.
If this property is already set in your elasticsearch.yml file, you're all set.
Otherwise, you can include it here in the watch JSON. {link}"
values={{
ymlValue: <EuiCode transparentBackground>message_defaults</EuiCode>,
link: (
<EuiLink href={documentationLinks.watcher.watchNotificationSettings} target="_blank">
<FormattedMessage
id="xpack.watcher.sections.watchEdit.json.slackActionValidationWarningMessage.helpLinkText"
defaultMessage="Learn more."
/>
</EuiLink>
)
}}
/>
);
errors.push({
message: i18n.translate('xpack.watcher.sections.watchEdit.json.warningPossibleInvalidSlackAction.description', {
// eslint-disable-next-line max-len
defaultMessage: 'This watch has a Slack action without a "to" property. This watch will only be valid if you specified the "to" property in the Slack "message_default" setting in Elasticsearch.'
})
message
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,47 @@

import { TIME_UNITS } from '../../../common/constants';
import moment from 'moment';
import { i18n } from '@kbn/i18n';

export class ExecuteDetails {
constructor(props = {}) {
this.triggeredTimeValue = props.triggeredTimeValue;
this.triggeredTimeUnit = props.triggeredTimeUnit;
this.scheduledTimeValue = props.scheduledTimeValue;
this.scheduledTimeUnit = props.scheduledTimeUnit;
this.scheduledTime = props.scheduledTime;
this.ignoreCondition = props.ignoreCondition;
this.alternativeInput = props.alternativeInput;
this.alternativeInput = props.alternativeInput || '';
this.actionModes = props.actionModes;
this.recordExecution = props.recordExecution;
}

validate() {
const errors = {
json: [],
};
if (this.alternativeInput || this.alternativeInput !== '') {
try {
const parsedJson = JSON.parse(this.alternativeInput);
if (parsedJson && typeof parsedJson !== 'object') {
errors.json.push(i18n.translate(
'xpack.watcher.sections.watchEdit.simulate.form.alternativeInputFieldError',
{
defaultMessage: 'Invalid JSON',
}
));
}
} catch (e) {
errors.json.push(i18n.translate(
'xpack.watcher.sections.watchEdit.simulate.form.alternativeInputFieldError',
{
defaultMessage: 'Invalid JSON',
}
));
}
}
return errors;
}

formatTime(timeUnit, value) {
let timeValue = moment();
switch (timeUnit) {
Expand All @@ -42,18 +69,17 @@ export class ExecuteDetails {
get upstreamJson() {
const hasTriggerTime = this.triggeredTimeValue !== '';
const hasScheduleTime = this.scheduledTimeValue !== '';
const formattedTriggerTime = hasTriggerTime ? this.formatTime(this.triggeredTimeUnit, this.triggeredTimeValue) : undefined;
const formattedScheduleTime = hasScheduleTime ? this.formatTime(this.scheduledTimeUnit, this.scheduledTimeValue) : undefined;
const triggerData = {
triggeredTime: formattedTriggerTime,
scheduledTime: formattedScheduleTime,
};
const triggeredTime = hasTriggerTime ? this.formatTime(this.triggeredTimeUnit, this.triggeredTimeValue) : undefined;
const scheduledTime = hasScheduleTime ? this.formatTime(this.scheduledTimeUnit, this.scheduledTimeValue) : undefined;
return {
triggerData,
triggerData: {
triggeredTime,
scheduledTime,
},
ignoreCondition: this.ignoreCondition,
alternativeInput: this.alternativeInput,
alternativeInput: this.alternativeInput !== '' ? JSON.parse(this.alternativeInput) : undefined,
actionModes: this.actionModes,
recordExecution: this.recordExecution
recordExecution: this.recordExecution,
};
}
}
3 changes: 1 addition & 2 deletions x-pack/plugins/watcher/public/models/watch/base_watch.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@ export class BaseWatch {
* @param {array} props.actions Action definitions
*/
constructor(props = {}) {
this.id = get(props, 'id', '');
this.id = get(props, 'id');
this.type = get(props, 'type');
this.isNew = get(props, 'isNew', true);

this.name = get(props, 'name');
this.isSystemWatch = Boolean(get(props, 'isSystemWatch'));
this.watchStatus = WatchStatus.fromUpstreamJson(get(props, 'watchStatus'));
Expand Down
67 changes: 64 additions & 3 deletions x-pack/plugins/watcher/public/models/watch/json_watch.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { get } from 'lodash';
import { BaseWatch } from './base_watch';
import { WATCH_TYPES } from 'plugins/watcher/../common/constants';
import { ACTION_TYPES, WATCH_TYPES } from 'plugins/watcher/../common/constants';
import defaultWatchJson from './default_watch.json';
import { i18n } from '@kbn/i18n';

Expand All @@ -17,16 +17,77 @@ export class JsonWatch extends BaseWatch {
constructor(props = {}) {
props.type = WATCH_TYPES.JSON;
super(props);
const existingWatch = get(props, 'watch');
this.watch = existingWatch ? existingWatch : defaultWatchJson;
this.watchString = get(props, 'watchString', JSON.stringify(existingWatch ? existingWatch : defaultWatchJson, null, 2));
}

this.watch = get(props, 'watch', defaultWatchJson);
validate() {
const validationResult = super.validate();
const idRegex = /^[A-Za-z0-9\-\_]+$/;
const errors = {
id: [],
json: [],
};
validationResult.errors = errors;
// Watch id validation
if (!this.id) {
errors.id.push(
i18n.translate('xpack.watcher.sections.watchEdit.json.error.requiredIdText', {
defaultMessage: 'ID is required',
})
);
} else if (!idRegex.test(this.id)) {
errors.id.push(i18n.translate('xpack.watcher.sections.watchEdit.json.error.invalidIdText', {
defaultMessage: 'ID can only contain letters, underscores, dashes, and numbers.',
}));
}
// JSON validation
if (!this.watchString || this.watchString === '') {
errors.json.push(i18n.translate('xpack.watcher.sections.watchEdit.json.error.requiredJsonText', {
defaultMessage: 'JSON is required',
}));
} else {
try {
const parsedJson = JSON.parse(this.watchString);
if (parsedJson && typeof parsedJson === 'object') {
const { actions } = parsedJson;
if (actions) {
// Validate if the action(s) provided is one of the supported actions
const invalidActions = Object.keys(actions).find(actionKey => {
const actionKeys = Object.keys(actions[actionKey]);
let type;
Object.keys(ACTION_TYPES).forEach(actionTypeKey => {
if (actionKeys.includes(ACTION_TYPES[actionTypeKey]) && !actionKeys.includes(ACTION_TYPES.UNKNOWN)) {
type = ACTION_TYPES[actionTypeKey];
}
});
return !type;
});
if (invalidActions) {
errors.json.push(i18n.translate('xpack.watcher.sections.watchEdit.json.error.invalidActionType', {
defaultMessage: 'Unknown action type provided for action "{action}".',
values: {
action: invalidActions,
},
}));
}
}
}
} catch (e) {
errors.json.push(i18n.translate('xpack.watcher.sections.watchEdit.json.error.invalidJsonText', {
defaultMessage: 'Invalid JSON',
}));
}
}
return validationResult;
}

get upstreamJson() {
const result = super.upstreamJson;
Object.assign(result, {
watch: this.watch
});

return result;
}

Expand Down
Loading