Skip to content

Commit

Permalink
Watcher port edit watch (#34633)
Browse files Browse the repository at this point in the history
* replacing Angular watch list UI with React one

* fixing TS issues

* addressing PR feedback

* fixing TS issues

* more TS fixes

* TS fix

* addressing more PR feedback

* TS fixes

* removing unused/incompatible translations

* fixing functional test

* prettier fix

* fixing typp

* fixing missing comma

* fixing data-test-subject typo

* fixing functional test

* fixing functional tests

* fixing delete functional tests

* addressing PR feedback

* progress on watch edit

* port advanced watcher to react (#34188)

* port advanced watcher to react

* fix i18n

* update execute trigger override text fields to number input and select fields

* fix page title for edit mode

* remove todo comments

* add license validity check; pass kbnUrl service as prop

* address review comments
  • Loading branch information
alisonelizabeth authored and bmcconaghy committed Apr 5, 2019
1 parent a170f7a commit a7ecccc
Show file tree
Hide file tree
Showing 31 changed files with 2,393 additions and 90 deletions.
1 change: 1 addition & 0 deletions x-pack/plugins/watcher/common/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ 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';
1 change: 1 addition & 0 deletions x-pack/plugins/watcher/common/constants/time_units.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

export const TIME_UNITS: { [key: string]: string } = {
MILLISECOND: 'ms',
SECOND: 's',
MINUTE: 'm',
HOUR: 'h',
Expand Down
30 changes: 30 additions & 0 deletions x-pack/plugins/watcher/common/constants/watch_tabs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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 { i18n } from '@kbn/i18n';

export const WATCH_TAB_ID_EDIT = 'watchEditTab';
export const WATCH_TAB_ID_SIMULATE = 'watchSimulateTab';

interface WatchTab {
id: string;
name: string;
}

export const WATCH_TABS: WatchTab[] = [
{
id: WATCH_TAB_ID_EDIT,
name: i18n.translate('xpack.watcher.sections.watchEdit.json.editTabLabel', {
defaultMessage: 'Edit',
}),
},
{
id: WATCH_TAB_ID_SIMULATE,
name: i18n.translate('xpack.watcher.sections.watchEdit.json.simulateTabLabel', {
defaultMessage: 'Simulate',
}),
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { keys, values, intersection } from 'lodash';
import { intersection, keys, values } from 'lodash';
import { ACTION_TYPES } from '../../constants';

export function getActionType(action) {
const type = intersection(
keys(action),
values(ACTION_TYPES)
)[0] || ACTION_TYPES.UNKNOWN;
export function getActionType(action: { [key: string]: { [key: string]: any } }) {
const type = intersection(keys(action), values(ACTION_TYPES))[0] || ACTION_TYPES.UNKNOWN;

return type;
}
57 changes: 57 additions & 0 deletions x-pack/plugins/watcher/common/types/watch_types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* 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.
*/

export interface ExecutedWatchResults {
id: string;
watchId: string;
details: any;
startTime: Date;
watchStatus: {
state: string;
actionStatuses: Array<{ state: string; lastExecutionReason: string }>;
};
}

export interface ExecutedWatchDetails {
triggerData: {
triggeredTime: Date;
scheduledTime: Date;
};
ignoreCondition: boolean;
alternativeInput: any;
actionModes: {
[key: string]: string;
};
recordExecution: boolean;
upstreamJson: any;
}

export interface BaseWatch {
id: string;
type: string;
isNew: boolean;
name: string;
isSystemWatch: boolean;
watchStatus: any;
watchErrors: any;
typeName: string;
displayName: string;
upstreamJson: any;
resetActions: () => void;
createAction: (type: string, actionProps: {}) => void;
validate: () => { warning: { message: string } };
actions: [
{
id: string;
type: string;
}
];
watch: {
actions: {
[key: string]: { [key: string]: any };
};
};
}
44 changes: 44 additions & 0 deletions x-pack/plugins/watcher/public/components/confirm_watches_modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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 { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';

export const ConfirmWatchesModal = ({
modalOptions,
callback,
}: {
modalOptions: { message: string } | null;
callback: (isConfirmed?: boolean) => void;
}) => {
if (!modalOptions) {
return null;
}
return (
<EuiOverlayMask>
<EuiConfirmModal
buttonColor="danger"
title={i18n.translate('xpack.watcher.sections.watchEdit.json.saveConfirmModal.title', {
defaultMessage: 'Confirm save',
})}
onCancel={() => callback()}
onConfirm={() => {
callback(true);
}}
cancelButtonText={i18n.translate(
'xpack.watcher.sections.watchEdit.json.saveConfirmModal.cancelButtonLabel',
{ defaultMessage: 'Cancel' }
)}
confirmButtonText={i18n.translate(
'xpack.watcher.sections.watchEdit.json.saveConfirmModal.saveButtonLabel',
{ defaultMessage: 'Save' }
)}
>
{modalOptions.message}
</EuiConfirmModal>
</EuiOverlayMask>
);
};
38 changes: 38 additions & 0 deletions x-pack/plugins/watcher/public/components/form_errors.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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 { EuiFormRow } from '@elastic/eui';
import React, { Children, cloneElement, Fragment, ReactElement } from 'react';

export const ErrableFormRow = ({
errorKey,
isShowingErrors,
errors,
children,
...rest
}: {
errorKey: string;
isShowingErrors: boolean;
errors: { [key: string]: string[] };
children: ReactElement;
[key: string]: any;
}) => {
return (
<EuiFormRow
isInvalid={isShowingErrors && errors[errorKey].length > 0}
error={errors[errorKey]}
{...rest}
>
<Fragment>
{Children.map(children, child =>
cloneElement(child, {
isInvalid: isShowingErrors && errors[errorKey].length > 0,
})
)}
</Fragment>
</EuiFormRow>
);
};
45 changes: 45 additions & 0 deletions x-pack/plugins/watcher/public/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Watch } from 'plugins/watcher/models/watch';
import { __await } from 'tslib';
import chrome from 'ui/chrome';
import { ROUTES } from '../../common/constants';
import { BaseWatch, ExecutedWatchDetails } from '../../common/types/watch_types';

let httpClient: ng.IHttpService;
export const setHttpClient = (anHttpClient: ng.IHttpService) => {
httpClient = anHttpClient;
Expand All @@ -31,3 +34,45 @@ export const deleteWatches = async (watchIds: string[]) => {
} = await getHttpClient().post(`${basePath}/watches/delete`, body);
return results;
};
export const fetchWatch = async (watchId: string) => {
const body = {
watchId,
};
const {
data: { results },
} = await getHttpClient().post(`${basePath}/watches/`, body);
return results;
};
export const loadWatch = async (id: string) => {
const { data: watch } = await getHttpClient().get(`${basePath}/watch/${id}`);
return Watch.fromUpstreamJson(watch.watch);
};
export const getMatchingIndices = async (pattern: string) => {
if (!pattern.startsWith('*')) {
pattern = `*${pattern}`;
}
if (!pattern.endsWith('*')) {
pattern = `${pattern}*`;
}
const {
data: { indices },
} = await getHttpClient().post(`${basePath}/indices`, { pattern });
return indices;
};
export const fetchFields = async (indexes: string[]) => {
const {
data: { fields },
} = await getHttpClient().post(`${basePath}/fields`, { indexes });
return fields;
};
export const createWatch = async (watch: BaseWatch) => {
const { data } = await getHttpClient().put(`${basePath}/watch/${watch.id}`, watch.upstreamJson);
return data;
};
export const executeWatch = async (executeWatchDetails: ExecutedWatchDetails, watch: BaseWatch) => {
const { data } = await getHttpClient().put(`${basePath}/watch/execute`, {
executeDetails: executeWatchDetails.upstreamJson,
watch: watch.upstreamJson,
});
return data;
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { makeDocumentationLink } from './make_documentation_link';

export const documentationLinks = {
watcher: {
putWatchApi: makeDocumentationLink('{baseUrl}guide/en/elasticsearch/reference/{urlVersion}/watcher-api-put-watch.html')
}
putWatchApi: makeDocumentationLink(
'{baseUrl}guide/en/elasticsearch/reference/{urlVersion}/watcher-api-put-watch.html'
),
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/

export { documentationLinks } from './documentation_links.js';
export { documentationLinks } from './documentation_links';
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,6 @@ const minor = semver.minor(metadata.version);
const urlVersion = `${major}.${minor}`;
const baseUrl = 'https://www.elastic.co/';

/**
*
* @param {string} linkTemplate Link template containing {baseUrl} and {urlVersion} placeholders
* @return {string} Actual link, with placeholders in template replaced
*/
export function makeDocumentationLink(linkTemplate) {
return linkTemplate
.replace('{baseUrl}', baseUrl)
.replace('{urlVersion}', urlVersion);
export function makeDocumentationLink(linkTemplate: string) {
return linkTemplate.replace('{baseUrl}', baseUrl).replace('{urlVersion}', urlVersion);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,52 @@
* you may not use this file except in compliance with the Elastic License.
*/

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

export class ExecuteDetails {
constructor(props = {}) {
this.triggeredTime = props.triggeredTime;
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.actionModes = props.actionModes;
this.recordExecution = props.recordExecution;
}

formatTime(timeUnit, value) {
let timeValue = moment();
switch (timeUnit) {
case TIME_UNITS.SECOND:
timeValue = timeValue.add(value, 'seconds');
break;
case TIME_UNITS.MINUTE:
timeValue = timeValue.add(value, 'minutes');
break;
case TIME_UNITS.HOUR:
timeValue = timeValue.add(value, 'hours');
break;
case TIME_UNITS.MILLISECOND:
timeValue = timeValue.add(value, 'milliseconds');
break;
}
return timeValue.format();
}

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: this.triggeredTime,
scheduledTime: this.scheduledTime,
triggeredTime: formattedTriggerTime,
scheduledTime: formattedScheduleTime,
};

return {
triggerData: triggerData,
triggerData,
ignoreCondition: this.ignoreCondition,
alternativeInput: this.alternativeInput,
actionModes: this.actionModes,
Expand Down
20 changes: 20 additions & 0 deletions x-pack/plugins/watcher/public/models/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,23 @@
declare module 'plugins/watcher/models/watch' {
export const Watch: any;
}
declare module 'plugins/watcher/models/watch/threshold_watch' {
export const ThresholdWatch: any;
}
declare module 'plugins/watcher/models/watch/json_watch' {
export const JsonWatch: any;
}

declare module 'plugins/watcher/models/execute_details/execute_details' {
export const ExecuteDetails: any;
}

declare module 'plugins/watcher/models/watch_history_item' {
export const WatchHistoryItem: any;
}

// TODO: Remove once typescript definitions are in EUI
declare module '@elastic/eui' {
export const EuiCodeEditor: React.SFC<any>;
export const EuiDescribedFormGroup: React.SFC<any>;
}
2 changes: 1 addition & 1 deletion x-pack/plugins/watcher/public/models/watch/base_watch.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ 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);

Expand Down
Loading

0 comments on commit a7ecccc

Please sign in to comment.