From 9802747d52b93260f8561702f742182f0b9f2bdb Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 4 May 2020 12:03:25 +0100 Subject: [PATCH 01/22] lazy load email action --- .../components/builtin_action_types/email.tsx | 609 ------------------ .../email/email_connector.test.tsx | 64 ++ .../email/email_connector.tsx | 210 ++++++ .../email/email_params.test.tsx | 59 ++ .../email/email_params.tsx | 265 ++++++++ .../{email.test.tsx => email/index.test.tsx} | 49 +- .../builtin_action_types/email/index.tsx | 162 +++++ 7 files changed, 764 insertions(+), 654 deletions(-) delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx rename x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/{email.test.tsx => email/index.test.tsx} (79%) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email.tsx deleted file mode 100644 index dff697297f3e48..00000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email.tsx +++ /dev/null @@ -1,609 +0,0 @@ -/* - * 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, useState, useEffect } from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { - EuiFieldText, - EuiFlexItem, - EuiFlexGroup, - EuiFieldNumber, - EuiFieldPassword, - EuiComboBox, - EuiTextArea, - EuiButtonEmpty, - EuiSwitch, - EuiFormRow, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { - ActionTypeModel, - ActionConnectorFieldsProps, - ValidationResult, - ActionParamsProps, -} from '../../../types'; -import { EmailActionParams, EmailActionConnector } from './types'; -import { AddMessageVariables } from '../add_message_variables'; - -export function getActionType(): ActionTypeModel { - const mailformat = /^[^@\s]+@[^@\s]+$/; - return { - id: '.email', - iconClass: 'email', - selectMessage: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.selectMessageText', - { - defaultMessage: 'Send email from your server.', - } - ), - actionTypeTitle: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.actionTypeTitle', - { - defaultMessage: 'Send to email', - } - ), - validateConnector: (action: EmailActionConnector): ValidationResult => { - const validationResult = { errors: {} }; - const errors = { - from: new Array(), - port: new Array(), - host: new Array(), - user: new Array(), - password: new Array(), - }; - validationResult.errors = errors; - if (!action.config.from) { - errors.from.push( - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredFromText', - { - defaultMessage: 'Sender is required.', - } - ) - ); - } - if (action.config.from && !action.config.from.trim().match(mailformat)) { - errors.from.push( - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.formatFromText', - { - defaultMessage: 'Sender is not a valid email address.', - } - ) - ); - } - if (!action.config.port) { - errors.port.push( - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredPortText', - { - defaultMessage: 'Port is required.', - } - ) - ); - } - if (!action.config.host) { - errors.host.push( - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredHostText', - { - defaultMessage: 'Host is required.', - } - ) - ); - } - if (action.secrets.user && !action.secrets.password) { - errors.password.push( - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredPasswordText', - { - defaultMessage: 'Password is required when username is used.', - } - ) - ); - } - if (!action.secrets.user && action.secrets.password) { - errors.user.push( - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredUserText', - { - defaultMessage: 'Username is required when password is used.', - } - ) - ); - } - return validationResult; - }, - validateParams: (actionParams: EmailActionParams): ValidationResult => { - const validationResult = { errors: {} }; - const errors = { - to: new Array(), - cc: new Array(), - bcc: new Array(), - message: new Array(), - subject: new Array(), - }; - validationResult.errors = errors; - if ( - (!(actionParams.to instanceof Array) || actionParams.to.length === 0) && - (!(actionParams.cc instanceof Array) || actionParams.cc.length === 0) && - (!(actionParams.bcc instanceof Array) || actionParams.bcc.length === 0) - ) { - const errorText = i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredEntryText', - { - defaultMessage: 'No To, Cc, or Bcc entry. At least one entry is required.', - } - ); - errors.to.push(errorText); - errors.cc.push(errorText); - errors.bcc.push(errorText); - } - if (!actionParams.message?.length) { - errors.message.push( - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredMessageText', - { - defaultMessage: 'Message is required.', - } - ) - ); - } - if (!actionParams.subject?.length) { - errors.subject.push( - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSubjectText', - { - defaultMessage: 'Subject is required.', - } - ) - ); - } - return validationResult; - }, - actionConnectorFields: EmailActionConnectorFields, - actionParamsFields: EmailParamsFields, - }; -} - -const EmailActionConnectorFields: React.FunctionComponent> = ({ action, editActionConfig, editActionSecrets, errors }) => { - const { from, host, port, secure } = action.config; - const { user, password } = action.secrets; - - return ( - - - - 0 && from !== undefined} - label={i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.fromTextFieldLabel', - { - defaultMessage: 'Sender', - } - )} - > - 0 && from !== undefined} - name="from" - value={from || ''} - data-test-subj="emailFromInput" - onChange={e => { - editActionConfig('from', e.target.value); - }} - onBlur={() => { - if (!from) { - editActionConfig('from', ''); - } - }} - /> - - - - - - 0 && host !== undefined} - label={i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.hostTextFieldLabel', - { - defaultMessage: 'Host', - } - )} - > - 0 && host !== undefined} - name="host" - value={host || ''} - data-test-subj="emailHostInput" - onChange={e => { - editActionConfig('host', e.target.value); - }} - onBlur={() => { - if (!host) { - editActionConfig('host', ''); - } - }} - /> - - - - - - 0 && port !== undefined} - label={i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.portTextFieldLabel', - { - defaultMessage: 'Port', - } - )} - > - 0 && port !== undefined} - fullWidth - name="port" - value={port || ''} - data-test-subj="emailPortInput" - onChange={e => { - editActionConfig('port', parseInt(e.target.value, 10)); - }} - onBlur={() => { - if (!port) { - editActionConfig('port', 0); - } - }} - /> - - - - - - { - editActionConfig('secure', e.target.checked); - }} - /> - - - - - - - - - 0} - label={i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.userTextFieldLabel', - { - defaultMessage: 'Username', - } - )} - > - 0} - name="user" - value={user || ''} - data-test-subj="emailUserInput" - onChange={e => { - editActionSecrets('user', nullableString(e.target.value)); - }} - /> - - - - 0} - label={i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.passwordFieldLabel', - { - defaultMessage: 'Password', - } - )} - > - 0} - name="password" - value={password || ''} - data-test-subj="emailPasswordInput" - onChange={e => { - editActionSecrets('password', nullableString(e.target.value)); - }} - /> - - - - - ); -}; - -const EmailParamsFields: React.FunctionComponent> = ({ - actionParams, - editAction, - index, - errors, - messageVariables, - defaultMessage, -}) => { - const { to, cc, bcc, subject, message } = actionParams; - const toOptions = to ? to.map((label: string) => ({ label })) : []; - const ccOptions = cc ? cc.map((label: string) => ({ label })) : []; - const bccOptions = bcc ? bcc.map((label: string) => ({ label })) : []; - const [addCC, setAddCC] = useState(false); - const [addBCC, setAddBCC] = useState(false); - - useEffect(() => { - if (!message && defaultMessage && defaultMessage.length > 0) { - editAction('message', defaultMessage, index); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const onSelectMessageVariable = (paramsProperty: string, variable: string) => { - editAction( - paramsProperty, - ((actionParams as any)[paramsProperty] ?? '').concat(` {{${variable}}}`), - index - ); - }; - - return ( - - 0 && to !== undefined} - label={i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.recipientTextFieldLabel', - { - defaultMessage: 'To', - } - )} - labelAppend={ - - - {!addCC ? ( - setAddCC(true)}> - - - ) : null} - {!addBCC ? ( - setAddBCC(true)}> - - - ) : null} - - - } - > - 0 && to !== undefined} - fullWidth - data-test-subj="toEmailAddressInput" - selectedOptions={toOptions} - onCreateOption={(searchValue: string) => { - const newOptions = [...toOptions, { label: searchValue }]; - editAction( - 'to', - newOptions.map(newOption => newOption.label), - index - ); - }} - onChange={(selectedOptions: Array<{ label: string }>) => { - editAction( - 'to', - selectedOptions.map(selectedOption => selectedOption.label), - index - ); - }} - onBlur={() => { - if (!to) { - editAction('to', [], index); - } - }} - /> - - {addCC ? ( - 0 && cc !== undefined} - label={i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.recipientCopyTextFieldLabel', - { - defaultMessage: 'Cc', - } - )} - > - 0 && cc !== undefined} - fullWidth - data-test-subj="ccEmailAddressInput" - selectedOptions={ccOptions} - onCreateOption={(searchValue: string) => { - const newOptions = [...ccOptions, { label: searchValue }]; - editAction( - 'cc', - newOptions.map(newOption => newOption.label), - index - ); - }} - onChange={(selectedOptions: Array<{ label: string }>) => { - editAction( - 'cc', - selectedOptions.map(selectedOption => selectedOption.label), - index - ); - }} - onBlur={() => { - if (!cc) { - editAction('cc', [], index); - } - }} - /> - - ) : null} - {addBCC ? ( - 0 && bcc !== undefined} - label={i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.recipientBccTextFieldLabel', - { - defaultMessage: 'Bcc', - } - )} - > - 0 && bcc !== undefined} - fullWidth - data-test-subj="bccEmailAddressInput" - selectedOptions={bccOptions} - onCreateOption={(searchValue: string) => { - const newOptions = [...bccOptions, { label: searchValue }]; - editAction( - 'bcc', - newOptions.map(newOption => newOption.label), - index - ); - }} - onChange={(selectedOptions: Array<{ label: string }>) => { - editAction( - 'bcc', - selectedOptions.map(selectedOption => selectedOption.label), - index - ); - }} - onBlur={() => { - if (!bcc) { - editAction('bcc', [], index); - } - }} - /> - - ) : null} - 0 && subject !== undefined} - label={i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.subjectTextFieldLabel', - { - defaultMessage: 'Subject', - } - )} - labelAppend={ - - onSelectMessageVariable('subject', variable) - } - paramsProperty="subject" - /> - } - > - 0 && subject !== undefined} - name="subject" - data-test-subj="emailSubjectInput" - value={subject || ''} - onChange={e => { - editAction('subject', e.target.value, index); - }} - onBlur={() => { - if (!subject) { - editAction('subject', '', index); - } - }} - /> - - 0 && message !== undefined} - label={i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.messageTextAreaFieldLabel', - { - defaultMessage: 'Message', - } - )} - labelAppend={ - - onSelectMessageVariable('message', variable) - } - paramsProperty="message" - /> - } - > - 0 && message !== undefined} - value={message || ''} - name="message" - data-test-subj="emailMessageInput" - onChange={e => { - editAction('message', e.target.value, index); - }} - onBlur={() => { - if (!message) { - editAction('message', '', index); - } - }} - /> - - - ); -}; - -// if the string == null or is empty, return null, else return string -function nullableString(str: string | null | undefined) { - if (str == null || str.trim() === '') return null; - return str; -} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx new file mode 100644 index 00000000000000..50973a43313780 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx @@ -0,0 +1,64 @@ +/* + * 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 from 'react'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { TypeRegistry } from '../../../type_registry'; +import { registerBuiltInActionTypes } from '../index'; +import { ActionTypeModel } from '../../../../types'; +import { EmailActionConnector } from '../types'; + +const ACTION_TYPE_ID = '.email'; +let actionTypeModel: ActionTypeModel; + +beforeAll(() => { + const actionTypeRegistry = new TypeRegistry(); + registerBuiltInActionTypes({ actionTypeRegistry }); + const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); + if (getResult !== null) { + actionTypeModel = getResult; + } +}); + +describe('EmailActionConnectorFields renders', () => { + test('all connector fields is rendered', () => { + expect(actionTypeModel.actionConnectorFields).not.toBeNull(); + if (!actionTypeModel.actionConnectorFields) { + return; + } + const ConnectorFields = actionTypeModel.actionConnectorFields; + const actionConnector = { + secrets: { + user: 'user', + password: 'pass', + }, + id: 'test', + actionTypeId: '.email', + name: 'email', + config: { + from: 'test@test.com', + }, + } as EmailActionConnector; + const wrapper = mountWithIntl( + {}} + editActionSecrets={() => {}} + /> + ); + expect(wrapper.find('[data-test-subj="emailFromInput"]').length > 0).toBeTruthy(); + expect( + wrapper + .find('[data-test-subj="emailFromInput"]') + .first() + .prop('value') + ).toBe('test@test.com'); + expect(wrapper.find('[data-test-subj="emailHostInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="emailPortInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="emailUserInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="emailPasswordInput"]').length > 0).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx new file mode 100644 index 00000000000000..aefce8c0ca5cfa --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx @@ -0,0 +1,210 @@ +/* + * 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 { + EuiFieldText, + EuiFlexItem, + EuiFlexGroup, + EuiFieldNumber, + EuiFieldPassword, + EuiSwitch, + EuiFormRow, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ActionConnectorFieldsProps } from '../../../../types'; +import { EmailActionConnector } from '../types'; + +// eslint-disable-next-line import/no-default-export +export default ({ + action, + editActionConfig, + editActionSecrets, + errors, +}: ActionConnectorFieldsProps) => { + const { from, host, port, secure } = action.config; + const { user, password } = action.secrets; + + return ( + + + + 0 && from !== undefined} + label={i18n.translate( + 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.fromTextFieldLabel', + { + defaultMessage: 'Sender', + } + )} + > + 0 && from !== undefined} + name="from" + value={from || ''} + data-test-subj="emailFromInput" + onChange={e => { + editActionConfig('from', e.target.value); + }} + onBlur={() => { + if (!from) { + editActionConfig('from', ''); + } + }} + /> + + + + + + 0 && host !== undefined} + label={i18n.translate( + 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.hostTextFieldLabel', + { + defaultMessage: 'Host', + } + )} + > + 0 && host !== undefined} + name="host" + value={host || ''} + data-test-subj="emailHostInput" + onChange={e => { + editActionConfig('host', e.target.value); + }} + onBlur={() => { + if (!host) { + editActionConfig('host', ''); + } + }} + /> + + + + + + 0 && port !== undefined} + label={i18n.translate( + 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.portTextFieldLabel', + { + defaultMessage: 'Port', + } + )} + > + 0 && port !== undefined} + fullWidth + name="port" + value={port || ''} + data-test-subj="emailPortInput" + onChange={e => { + editActionConfig('port', parseInt(e.target.value, 10)); + }} + onBlur={() => { + if (!port) { + editActionConfig('port', 0); + } + }} + /> + + + + + + { + editActionConfig('secure', e.target.checked); + }} + /> + + + + + + + + + 0} + label={i18n.translate( + 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.userTextFieldLabel', + { + defaultMessage: 'Username', + } + )} + > + 0} + name="user" + value={user || ''} + data-test-subj="emailUserInput" + onChange={e => { + editActionSecrets('user', nullableString(e.target.value)); + }} + /> + + + + 0} + label={i18n.translate( + 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.passwordFieldLabel', + { + defaultMessage: 'Password', + } + )} + > + 0} + name="password" + value={password || ''} + data-test-subj="emailPasswordInput" + onChange={e => { + editActionSecrets('password', nullableString(e.target.value)); + }} + /> + + + + + ); +}; + +// if the string == null or is empty, return null, else return string +function nullableString(str: string | null | undefined) { + if (str == null || str.trim() === '') return null; + return str; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.test.tsx new file mode 100644 index 00000000000000..ccf0ed80b3523d --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.test.tsx @@ -0,0 +1,59 @@ +/* + * 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, { FunctionComponent } from 'react'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { TypeRegistry } from '../../../type_registry'; +import { registerBuiltInActionTypes } from '../index'; +import { ActionTypeModel, ActionParamsProps } from '../../../../types'; +import { EmailActionParams } from '../types'; + +const ACTION_TYPE_ID = '.email'; +let actionTypeModel: ActionTypeModel; + +beforeAll(() => { + const actionTypeRegistry = new TypeRegistry(); + registerBuiltInActionTypes({ actionTypeRegistry }); + const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); + if (getResult !== null) { + actionTypeModel = getResult; + } +}); + +describe('EmailParamsFields renders', () => { + test('all params fields is rendered', () => { + expect(actionTypeModel.actionParamsFields).not.toBeNull(); + if (!actionTypeModel.actionParamsFields) { + return; + } + const ParamsFields = actionTypeModel.actionParamsFields as FunctionComponent< + ActionParamsProps + >; + const actionParams = { + cc: [], + bcc: [], + to: ['test@test.com'], + subject: 'test', + message: 'test message', + }; + const wrapper = mountWithIntl( + {}} + index={0} + /> + ); + expect(wrapper.find('[data-test-subj="toEmailAddressInput"]').length > 0).toBeTruthy(); + expect( + wrapper + .find('[data-test-subj="toEmailAddressInput"]') + .first() + .prop('selectedOptions') + ).toStrictEqual([{ label: 'test@test.com' }]); + expect(wrapper.find('[data-test-subj="emailSubjectInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="emailMessageInput"]').length > 0).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx new file mode 100644 index 00000000000000..5a91beccec0f4f --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx @@ -0,0 +1,265 @@ +/* + * 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, useState, useEffect } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiFieldText, EuiComboBox, EuiTextArea, EuiButtonEmpty, EuiFormRow } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ActionParamsProps } from '../../../../types'; +import { EmailActionParams } from '../types'; +import { AddMessageVariables } from '../../add_message_variables'; + +// eslint-disable-next-line import/no-default-export +export default ({ + actionParams, + editAction, + index, + errors, + messageVariables, + defaultMessage, +}: ActionParamsProps) => { + const { to, cc, bcc, subject, message } = actionParams; + const toOptions = to ? to.map((label: string) => ({ label })) : []; + const ccOptions = cc ? cc.map((label: string) => ({ label })) : []; + const bccOptions = bcc ? bcc.map((label: string) => ({ label })) : []; + const [addCC, setAddCC] = useState(false); + const [addBCC, setAddBCC] = useState(false); + + useEffect(() => { + if (!message && defaultMessage && defaultMessage.length > 0) { + editAction('message', defaultMessage, index); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const onSelectMessageVariable = (paramsProperty: string, variable: string) => { + editAction( + paramsProperty, + ((actionParams as any)[paramsProperty] ?? '').concat(` {{${variable}}}`), + index + ); + }; + + return ( + + 0 && to !== undefined} + label={i18n.translate( + 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.recipientTextFieldLabel', + { + defaultMessage: 'To', + } + )} + labelAppend={ + + + {!addCC ? ( + setAddCC(true)}> + + + ) : null} + {!addBCC ? ( + setAddBCC(true)}> + + + ) : null} + + + } + > + 0 && to !== undefined} + fullWidth + data-test-subj="toEmailAddressInput" + selectedOptions={toOptions} + onCreateOption={(searchValue: string) => { + const newOptions = [...toOptions, { label: searchValue }]; + editAction( + 'to', + newOptions.map(newOption => newOption.label), + index + ); + }} + onChange={(selectedOptions: Array<{ label: string }>) => { + editAction( + 'to', + selectedOptions.map(selectedOption => selectedOption.label), + index + ); + }} + onBlur={() => { + if (!to) { + editAction('to', [], index); + } + }} + /> + + {addCC ? ( + 0 && cc !== undefined} + label={i18n.translate( + 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.recipientCopyTextFieldLabel', + { + defaultMessage: 'Cc', + } + )} + > + 0 && cc !== undefined} + fullWidth + data-test-subj="ccEmailAddressInput" + selectedOptions={ccOptions} + onCreateOption={(searchValue: string) => { + const newOptions = [...ccOptions, { label: searchValue }]; + editAction( + 'cc', + newOptions.map(newOption => newOption.label), + index + ); + }} + onChange={(selectedOptions: Array<{ label: string }>) => { + editAction( + 'cc', + selectedOptions.map(selectedOption => selectedOption.label), + index + ); + }} + onBlur={() => { + if (!cc) { + editAction('cc', [], index); + } + }} + /> + + ) : null} + {addBCC ? ( + 0 && bcc !== undefined} + label={i18n.translate( + 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.recipientBccTextFieldLabel', + { + defaultMessage: 'Bcc', + } + )} + > + 0 && bcc !== undefined} + fullWidth + data-test-subj="bccEmailAddressInput" + selectedOptions={bccOptions} + onCreateOption={(searchValue: string) => { + const newOptions = [...bccOptions, { label: searchValue }]; + editAction( + 'bcc', + newOptions.map(newOption => newOption.label), + index + ); + }} + onChange={(selectedOptions: Array<{ label: string }>) => { + editAction( + 'bcc', + selectedOptions.map(selectedOption => selectedOption.label), + index + ); + }} + onBlur={() => { + if (!bcc) { + editAction('bcc', [], index); + } + }} + /> + + ) : null} + 0 && subject !== undefined} + label={i18n.translate( + 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.subjectTextFieldLabel', + { + defaultMessage: 'Subject', + } + )} + labelAppend={ + + onSelectMessageVariable('subject', variable) + } + paramsProperty="subject" + /> + } + > + 0 && subject !== undefined} + name="subject" + data-test-subj="emailSubjectInput" + value={subject || ''} + onChange={e => { + editAction('subject', e.target.value, index); + }} + onBlur={() => { + if (!subject) { + editAction('subject', '', index); + } + }} + /> + + 0 && message !== undefined} + label={i18n.translate( + 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.messageTextAreaFieldLabel', + { + defaultMessage: 'Message', + } + )} + labelAppend={ + + onSelectMessageVariable('message', variable) + } + paramsProperty="message" + /> + } + > + 0 && message !== undefined} + value={message || ''} + name="message" + data-test-subj="emailMessageInput" + onChange={e => { + editAction('message', e.target.value, index); + }} + onBlur={() => { + if (!message) { + editAction('message', '', index); + } + }} + /> + + + ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.test.tsx similarity index 79% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email.test.tsx rename to x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.test.tsx index af9e34071fd095..17dde6149380fb 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.test.tsx @@ -5,10 +5,10 @@ */ import React, { FunctionComponent } from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { TypeRegistry } from '../../type_registry'; -import { registerBuiltInActionTypes } from './index'; -import { ActionTypeModel, ActionParamsProps } from '../../../types'; -import { EmailActionParams, EmailActionConnector } from './types'; +import { TypeRegistry } from '../../../type_registry'; +import { registerBuiltInActionTypes } from '../index'; +import { ActionTypeModel, ActionParamsProps } from '../../../../types'; +import { EmailActionParams, EmailActionConnector } from '../types'; const ACTION_TYPE_ID = '.email'; let actionTypeModel: ActionTypeModel; @@ -207,47 +207,6 @@ describe('action params validation', () => { }); }); -describe('EmailActionConnectorFields renders', () => { - test('all connector fields is rendered', () => { - expect(actionTypeModel.actionConnectorFields).not.toBeNull(); - if (!actionTypeModel.actionConnectorFields) { - return; - } - const ConnectorFields = actionTypeModel.actionConnectorFields; - const actionConnector = { - secrets: { - user: 'user', - password: 'pass', - }, - id: 'test', - actionTypeId: '.email', - name: 'email', - config: { - from: 'test@test.com', - }, - } as EmailActionConnector; - const wrapper = mountWithIntl( - {}} - editActionSecrets={() => {}} - /> - ); - expect(wrapper.find('[data-test-subj="emailFromInput"]').length > 0).toBeTruthy(); - expect( - wrapper - .find('[data-test-subj="emailFromInput"]') - .first() - .prop('value') - ).toBe('test@test.com'); - expect(wrapper.find('[data-test-subj="emailHostInput"]').length > 0).toBeTruthy(); - expect(wrapper.find('[data-test-subj="emailPortInput"]').length > 0).toBeTruthy(); - expect(wrapper.find('[data-test-subj="emailUserInput"]').length > 0).toBeTruthy(); - expect(wrapper.find('[data-test-subj="emailPasswordInput"]').length > 0).toBeTruthy(); - }); -}); - describe('EmailParamsFields renders', () => { test('all params fields is rendered', () => { expect(actionTypeModel.actionParamsFields).not.toBeNull(); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.tsx new file mode 100644 index 00000000000000..d572fb5cd17214 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.tsx @@ -0,0 +1,162 @@ +/* + * 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, { lazy, Suspense } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ActionTypeModel, ValidationResult } from '../../../../types'; +import { EmailActionParams, EmailActionConnector } from '../types'; + +const EmailActionConnectorFields = lazy(() => import('./email_connector')); +const EmailParamsFields = lazy(() => import('./email_params')); + +export function getActionType(): ActionTypeModel { + const mailformat = /^[^@\s]+@[^@\s]+$/; + return { + id: '.email', + iconClass: 'email', + selectMessage: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.selectMessageText', + { + defaultMessage: 'Send email from your server.', + } + ), + actionTypeTitle: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.actionTypeTitle', + { + defaultMessage: 'Send to email', + } + ), + validateConnector: (action: EmailActionConnector): ValidationResult => { + const validationResult = { errors: {} }; + const errors = { + from: new Array(), + port: new Array(), + host: new Array(), + user: new Array(), + password: new Array(), + }; + validationResult.errors = errors; + if (!action.config.from) { + errors.from.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredFromText', + { + defaultMessage: 'Sender is required.', + } + ) + ); + } + if (action.config.from && !action.config.from.trim().match(mailformat)) { + errors.from.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.error.formatFromText', + { + defaultMessage: 'Sender is not a valid email address.', + } + ) + ); + } + if (!action.config.port) { + errors.port.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredPortText', + { + defaultMessage: 'Port is required.', + } + ) + ); + } + if (!action.config.host) { + errors.host.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredHostText', + { + defaultMessage: 'Host is required.', + } + ) + ); + } + if (action.secrets.user && !action.secrets.password) { + errors.password.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredPasswordText', + { + defaultMessage: 'Password is required when username is used.', + } + ) + ); + } + if (!action.secrets.user && action.secrets.password) { + errors.user.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredUserText', + { + defaultMessage: 'Username is required when password is used.', + } + ) + ); + } + return validationResult; + }, + validateParams: (actionParams: EmailActionParams): ValidationResult => { + const validationResult = { errors: {} }; + const errors = { + to: new Array(), + cc: new Array(), + bcc: new Array(), + message: new Array(), + subject: new Array(), + }; + validationResult.errors = errors; + if ( + (!(actionParams.to instanceof Array) || actionParams.to.length === 0) && + (!(actionParams.cc instanceof Array) || actionParams.cc.length === 0) && + (!(actionParams.bcc instanceof Array) || actionParams.bcc.length === 0) + ) { + const errorText = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredEntryText', + { + defaultMessage: 'No To, Cc, or Bcc entry. At least one entry is required.', + } + ); + errors.to.push(errorText); + errors.cc.push(errorText); + errors.bcc.push(errorText); + } + if (!actionParams.message?.length) { + errors.message.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredMessageText', + { + defaultMessage: 'Message is required.', + } + ) + ); + } + if (!actionParams.subject?.length) { + errors.subject.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSubjectText', + { + defaultMessage: 'Subject is required.', + } + ) + ); + } + return validationResult; + }, + actionConnectorFields: (props: any) => ( + }> + + + ), + actionParamsFields: (props: any) => ( + }> + + + ), + }; +} From e6ec3f6013a34c6b7939a0bea706f0a0210d6b13 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 4 May 2020 15:38:45 +0100 Subject: [PATCH 02/22] fixed unit testing of lazy components --- .../email/email_connector.test.tsx | 23 ++---------- .../email/email_params.test.tsx | 28 ++------------- .../builtin_action_types/email/index.test.tsx | 36 ------------------- 3 files changed, 5 insertions(+), 82 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx index 50973a43313780..bddd49fb65befc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx @@ -5,30 +5,11 @@ */ import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { TypeRegistry } from '../../../type_registry'; -import { registerBuiltInActionTypes } from '../index'; -import { ActionTypeModel } from '../../../../types'; import { EmailActionConnector } from '../types'; - -const ACTION_TYPE_ID = '.email'; -let actionTypeModel: ActionTypeModel; - -beforeAll(() => { - const actionTypeRegistry = new TypeRegistry(); - registerBuiltInActionTypes({ actionTypeRegistry }); - const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); - if (getResult !== null) { - actionTypeModel = getResult; - } -}); +import EmailActionConnectorFields from './email_connector'; describe('EmailActionConnectorFields renders', () => { test('all connector fields is rendered', () => { - expect(actionTypeModel.actionConnectorFields).not.toBeNull(); - if (!actionTypeModel.actionConnectorFields) { - return; - } - const ConnectorFields = actionTypeModel.actionConnectorFields; const actionConnector = { secrets: { user: 'user', @@ -42,7 +23,7 @@ describe('EmailActionConnectorFields renders', () => { }, } as EmailActionConnector; const wrapper = mountWithIntl( - {}} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.test.tsx index ccf0ed80b3523d..a2b5ccf988afb0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.test.tsx @@ -3,34 +3,12 @@ * 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, { FunctionComponent } from 'react'; +import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { TypeRegistry } from '../../../type_registry'; -import { registerBuiltInActionTypes } from '../index'; -import { ActionTypeModel, ActionParamsProps } from '../../../../types'; -import { EmailActionParams } from '../types'; - -const ACTION_TYPE_ID = '.email'; -let actionTypeModel: ActionTypeModel; - -beforeAll(() => { - const actionTypeRegistry = new TypeRegistry(); - registerBuiltInActionTypes({ actionTypeRegistry }); - const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); - if (getResult !== null) { - actionTypeModel = getResult; - } -}); +import EmailParamsFields from './email_params'; describe('EmailParamsFields renders', () => { test('all params fields is rendered', () => { - expect(actionTypeModel.actionParamsFields).not.toBeNull(); - if (!actionTypeModel.actionParamsFields) { - return; - } - const ParamsFields = actionTypeModel.actionParamsFields as FunctionComponent< - ActionParamsProps - >; const actionParams = { cc: [], bcc: [], @@ -39,7 +17,7 @@ describe('EmailParamsFields renders', () => { message: 'test message', }; const wrapper = mountWithIntl( - {}} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.test.tsx index 17dde6149380fb..dfbf4711a6825b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.test.tsx @@ -206,39 +206,3 @@ describe('action params validation', () => { }); }); }); - -describe('EmailParamsFields renders', () => { - test('all params fields is rendered', () => { - expect(actionTypeModel.actionParamsFields).not.toBeNull(); - if (!actionTypeModel.actionParamsFields) { - return; - } - const ParamsFields = actionTypeModel.actionParamsFields as FunctionComponent< - ActionParamsProps - >; - const actionParams = { - cc: [], - bcc: [], - to: ['test@test.com'], - subject: 'test', - message: 'test message', - }; - const wrapper = mountWithIntl( - {}} - index={0} - /> - ); - expect(wrapper.find('[data-test-subj="toEmailAddressInput"]').length > 0).toBeTruthy(); - expect( - wrapper - .find('[data-test-subj="toEmailAddressInput"]') - .first() - .prop('selectedOptions') - ).toStrictEqual([{ label: 'test@test.com' }]); - expect(wrapper.find('[data-test-subj="emailSubjectInput"]').length > 0).toBeTruthy(); - expect(wrapper.find('[data-test-subj="emailMessageInput"]').length > 0).toBeTruthy(); - }); -}); From b717d3110ffceac0529014326b43e9b95497a15c Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 4 May 2020 17:02:55 +0100 Subject: [PATCH 03/22] remoed uneeded mports --- .../components/builtin_action_types/email/index.test.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.test.tsx index dfbf4711a6825b..e823e848f52c2c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.test.tsx @@ -3,12 +3,10 @@ * 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, { FunctionComponent } from 'react'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { TypeRegistry } from '../../../type_registry'; import { registerBuiltInActionTypes } from '../index'; -import { ActionTypeModel, ActionParamsProps } from '../../../../types'; -import { EmailActionParams, EmailActionConnector } from '../types'; +import { ActionTypeModel } from '../../../../types'; +import { EmailActionConnector } from '../types'; const ACTION_TYPE_ID = '.email'; let actionTypeModel: ActionTypeModel; From 9bdea730687308fcac9765e431d90a8b4bf6390c Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Tue, 5 May 2020 12:35:32 +0100 Subject: [PATCH 04/22] lazy load all actions --- .../email/{index.test.tsx => email.test.tsx} | 0 .../email/{index.tsx => email.tsx} | 0 .../email/email_connector.tsx | 6 +- .../email/email_params.tsx | 6 +- .../builtin_action_types/es_index.test.tsx | 221 ---------- .../es_index/es_index.test.tsx | 88 ++++ .../es_index/es_index.tsx | 63 +++ .../es_index/es_index_connector.test.tsx | 104 +++++ .../es_index_connector.tsx} | 138 +----- .../es_index/es_index_params.test.tsx | 33 ++ .../es_index/es_index_params.tsx | 81 ++++ .../components/builtin_action_types/index.ts | 12 +- .../builtin_action_types/pagerduty.test.tsx | 189 -------- .../{ => pagerduty}/pagerduty.svg | 0 .../pagerduty/pagerduty.test.tsx | 106 +++++ .../pagerduty/pagerduty.tsx | 108 +++++ .../pagerduty/pagerduty_connectors.test.tsx | 41 ++ .../pagerduty_connectors.tsx} | 95 +--- .../pagerduty/pagerduty_params.test.tsx | 56 +++ .../pagerduty/pagerduty_params.tsx | 414 ++++++++++++++++++ .../builtin_action_types/server_log.test.tsx | 134 ------ .../server_log/server_log.test.tsx | 71 +++ .../server_log/server_log.tsx | 58 +++ .../server_log/server_log_params.test.tsx | 58 +++ .../server_log_params.tsx} | 51 +-- .../components/builtin_action_types/slack.tsx | 188 -------- .../{ => slack}/slack.test.tsx | 72 +-- .../builtin_action_types/slack/slack.tsx | 78 ++++ .../slack/slack_connectors.test.tsx | 38 ++ .../slack/slack_connectors.tsx | 65 +++ .../slack/slack_params.test.tsx | 31 ++ .../slack/slack_params.tsx | 77 ++++ .../{ => webhook}/webhook.test.tsx | 81 +--- .../builtin_action_types/webhook/webhook.tsx | 111 +++++ .../webhook/webhook_connectors.test.tsx | 46 ++ .../webhook_connectors.tsx} | 156 +------ .../webhook/webhook_params.test.tsx | 32 ++ .../webhook/webhook_params.tsx | 68 +++ 38 files changed, 1880 insertions(+), 1296 deletions(-) rename x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/{index.test.tsx => email.test.tsx} (100%) rename x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/{index.tsx => email.tsx} (100%) delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.test.tsx rename x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/{es_index.tsx => es_index/es_index_connector.tsx} (64%) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.tsx delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.test.tsx rename x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/{ => pagerduty}/pagerduty.svg (100%) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.test.tsx rename x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/{pagerduty.tsx => pagerduty/pagerduty_connectors.tsx} (83%) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.test.tsx rename x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/{server_log.tsx => server_log/server_log_params.tsx} (67%) delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack.tsx rename x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/{ => slack}/slack.test.tsx (53%) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.tsx rename x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/{ => webhook}/webhook.test.tsx (50%) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx rename x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/{webhook.tsx => webhook/webhook_connectors.tsx} (71%) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.test.tsx similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.test.tsx rename to x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.test.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.tsx rename to x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx index aefce8c0ca5cfa..e41661ca36007e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx @@ -17,8 +17,7 @@ import { i18n } from '@kbn/i18n'; import { ActionConnectorFieldsProps } from '../../../../types'; import { EmailActionConnector } from '../types'; -// eslint-disable-next-line import/no-default-export -export default ({ +export const EmailActionConnectorFields = ({ action, editActionConfig, editActionSecrets, @@ -208,3 +207,6 @@ function nullableString(str: string | null | undefined) { if (str == null || str.trim() === '') return null; return str; } + +// eslint-disable-next-line import/no-default-export +export { EmailActionConnectorFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx index 5a91beccec0f4f..13e791f1069e3e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx @@ -11,8 +11,7 @@ import { ActionParamsProps } from '../../../../types'; import { EmailActionParams } from '../types'; import { AddMessageVariables } from '../../add_message_variables'; -// eslint-disable-next-line import/no-default-export -export default ({ +export const EmailParamsFields = ({ actionParams, editAction, index, @@ -263,3 +262,6 @@ export default ({ ); }; + +// eslint-disable-next-line import/no-default-export +export { EmailParamsFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.test.tsx deleted file mode 100644 index fdfaf70648694b..00000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.test.tsx +++ /dev/null @@ -1,221 +0,0 @@ -/* - * 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, { FunctionComponent } from 'react'; -import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; -import { act } from 'react-dom/test-utils'; -import { TypeRegistry } from '../../type_registry'; -import { registerBuiltInActionTypes } from './index'; -import { ActionTypeModel, ActionParamsProps } from '../../../types'; -import { IndexActionParams, EsIndexActionConnector } from './types'; -import { coreMock } from '../../../../../../../src/core/public/mocks'; -jest.mock('../../../common/index_controls', () => ({ - firstFieldOption: jest.fn(), - getFields: jest.fn(), - getIndexOptions: jest.fn(), - getIndexPatterns: jest.fn(), -})); - -const ACTION_TYPE_ID = '.index'; -let actionTypeModel: ActionTypeModel; - -beforeAll(() => { - const actionTypeRegistry = new TypeRegistry(); - registerBuiltInActionTypes({ actionTypeRegistry }); - const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); - if (getResult !== null) { - actionTypeModel = getResult; - } -}); - -describe('actionTypeRegistry.get() works', () => { - test('action type .index is registered', () => { - expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); - expect(actionTypeModel.iconClass).toEqual('indexOpen'); - }); -}); - -describe('index connector validation', () => { - test('connector validation succeeds when connector config is valid', () => { - const actionConnector = { - secrets: {}, - id: 'test', - actionTypeId: '.index', - name: 'es_index', - config: { - index: 'test_es_index', - refresh: false, - executionTimeField: '1', - }, - } as EsIndexActionConnector; - - expect(actionTypeModel.validateConnector(actionConnector)).toEqual({ - errors: { - index: [], - }, - }); - }); -}); - -describe('index connector validation with minimal config', () => { - test('connector validation succeeds when connector config is valid', () => { - const actionConnector = { - secrets: {}, - id: 'test', - actionTypeId: '.index', - name: 'es_index', - config: { - index: 'test_es_index', - }, - } as EsIndexActionConnector; - - expect(actionTypeModel.validateConnector(actionConnector)).toEqual({ - errors: { - index: [], - }, - }); - }); -}); - -describe('action params validation', () => { - test('action params validation succeeds when action params is valid', () => { - const actionParams = { - documents: ['test'], - }; - - expect(actionTypeModel.validateParams(actionParams)).toEqual({ - errors: {}, - }); - - const emptyActionParams = {}; - - expect(actionTypeModel.validateParams(emptyActionParams)).toEqual({ - errors: {}, - }); - }); -}); - -describe('IndexActionConnectorFields renders', () => { - test('all connector fields is rendered', async () => { - const mocks = coreMock.createSetup(); - - expect(actionTypeModel.actionConnectorFields).not.toBeNull(); - if (!actionTypeModel.actionConnectorFields) { - return; - } - - const { getIndexPatterns } = jest.requireMock('../../../common/index_controls'); - getIndexPatterns.mockResolvedValueOnce([ - { - id: 'indexPattern1', - attributes: { - title: 'indexPattern1', - }, - }, - { - id: 'indexPattern2', - attributes: { - title: 'indexPattern2', - }, - }, - ]); - const { getFields } = jest.requireMock('../../../common/index_controls'); - getFields.mockResolvedValueOnce([ - { - type: 'date', - name: 'test1', - }, - { - type: 'text', - name: 'test2', - }, - ]); - const ConnectorFields = actionTypeModel.actionConnectorFields; - const actionConnector = { - secrets: {}, - id: 'test', - actionTypeId: '.index', - name: 'es_index', - config: { - index: 'test', - refresh: false, - executionTimeField: 'test1', - }, - } as EsIndexActionConnector; - const wrapper = mountWithIntl( - {}} - editActionSecrets={() => {}} - http={mocks.http} - /> - ); - - await act(async () => { - await nextTick(); - wrapper.update(); - }); - - expect(wrapper.find('[data-test-subj="connectorIndexesComboBox"]').length > 0).toBeTruthy(); - expect(wrapper.find('[data-test-subj="indexRefreshCheckbox"]').length > 0).toBeTruthy(); - - const indexSearchBoxValue = wrapper.find('[data-test-subj="comboBoxSearchInput"]'); - expect(indexSearchBoxValue.first().props().value).toEqual(''); - - const indexComboBox = wrapper.find('#indexConnectorSelectSearchBox'); - indexComboBox.first().simulate('click'); - const event = { target: { value: 'indexPattern1' } }; - indexComboBox - .find('input') - .first() - .simulate('change', event); - - const indexSearchBoxValueBeforeEnterData = wrapper.find( - '[data-test-subj="comboBoxSearchInput"]' - ); - expect(indexSearchBoxValueBeforeEnterData.first().props().value).toEqual('indexPattern1'); - - const indexComboBoxClear = wrapper.find('[data-test-subj="comboBoxClearButton"]'); - indexComboBoxClear.first().simulate('click'); - - const indexSearchBoxValueAfterEnterData = wrapper.find( - '[data-test-subj="comboBoxSearchInput"]' - ); - expect(indexSearchBoxValueAfterEnterData.first().props().value).toEqual('indexPattern1'); - }); -}); - -describe('IndexParamsFields renders', () => { - test('all params fields is rendered', () => { - expect(actionTypeModel.actionParamsFields).not.toBeNull(); - if (!actionTypeModel.actionParamsFields) { - return; - } - const ParamsFields = actionTypeModel.actionParamsFields as FunctionComponent< - ActionParamsProps - >; - const actionParams = { - documents: [{ test: 123 }], - }; - const wrapper = mountWithIntl( - {}} - index={0} - /> - ); - expect( - wrapper - .find('[data-test-subj="actionIndexDoc"]') - .first() - .prop('value') - ).toBe(`{ - "test": 123 -}`); - expect(wrapper.find('[data-test-subj="documentsAddVariableButton"]').length > 0).toBeTruthy(); - }); -}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.test.tsx new file mode 100644 index 00000000000000..417a9e09086a2c --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.test.tsx @@ -0,0 +1,88 @@ +/* + * 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 { TypeRegistry } from '../../../type_registry'; +import { registerBuiltInActionTypes } from '../index'; +import { ActionTypeModel } from '../../../../types'; +import { EsIndexActionConnector } from '../types'; + +const ACTION_TYPE_ID = '.index'; +let actionTypeModel: ActionTypeModel; + +beforeAll(() => { + const actionTypeRegistry = new TypeRegistry(); + registerBuiltInActionTypes({ actionTypeRegistry }); + const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); + if (getResult !== null) { + actionTypeModel = getResult; + } +}); + +describe('actionTypeRegistry.get() works', () => { + test('action type .index is registered', () => { + expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); + expect(actionTypeModel.iconClass).toEqual('indexOpen'); + }); +}); + +describe('index connector validation', () => { + test('connector validation succeeds when connector config is valid', () => { + const actionConnector = { + secrets: {}, + id: 'test', + actionTypeId: '.index', + name: 'es_index', + config: { + index: 'test_es_index', + refresh: false, + executionTimeField: '1', + }, + } as EsIndexActionConnector; + + expect(actionTypeModel.validateConnector(actionConnector)).toEqual({ + errors: { + index: [], + }, + }); + }); +}); + +describe('index connector validation with minimal config', () => { + test('connector validation succeeds when connector config is valid', () => { + const actionConnector = { + secrets: {}, + id: 'test', + actionTypeId: '.index', + name: 'es_index', + config: { + index: 'test_es_index', + }, + } as EsIndexActionConnector; + + expect(actionTypeModel.validateConnector(actionConnector)).toEqual({ + errors: { + index: [], + }, + }); + }); +}); + +describe('action params validation', () => { + test('action params validation succeeds when action params is valid', () => { + const actionParams = { + documents: ['test'], + }; + + expect(actionTypeModel.validateParams(actionParams)).toEqual({ + errors: {}, + }); + + const emptyActionParams = {}; + + expect(actionTypeModel.validateParams(emptyActionParams)).toEqual({ + errors: {}, + }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.tsx new file mode 100644 index 00000000000000..3ee2c4dd4b7346 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.tsx @@ -0,0 +1,63 @@ +/* + * 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, { lazy, Suspense } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ActionTypeModel, ValidationResult } from '../../../../types'; +import { EsIndexActionConnector } from '.././types'; + +const IndexActionConnectorFields = lazy(() => import('./es_index_connector')); +const IndexParamsFields = lazy(() => import('./es_index_params')); + +export function getActionType(): ActionTypeModel { + return { + id: '.index', + iconClass: 'indexOpen', + selectMessage: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.selectMessageText', + { + defaultMessage: 'Index data into Elasticsearch.', + } + ), + actionTypeTitle: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.actionTypeTitle', + { + defaultMessage: 'Index data', + } + ), + validateConnector: (action: EsIndexActionConnector): ValidationResult => { + const validationResult = { errors: {} }; + const errors = { + index: new Array(), + }; + validationResult.errors = errors; + if (!action.config.index) { + errors.index.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.error.requiredIndexText', + { + defaultMessage: 'Index is required.', + } + ) + ); + } + return validationResult; + }, + actionConnectorFields: (props: any) => ( + }> + + + ), + actionParamsFields: (props: any) => ( + }> + + + ), + validateParams: (): ValidationResult => { + return { errors: {} }; + }, + }; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.test.tsx new file mode 100644 index 00000000000000..4afd62f237e5e9 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.test.tsx @@ -0,0 +1,104 @@ +/* + * 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 from 'react'; +import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; +import { act } from 'react-dom/test-utils'; +import { EsIndexActionConnector } from '../types'; +import { coreMock } from '../../../../../../../../src/core/public/mocks'; +import IndexActionConnectorFields from './es_index_connector'; + +jest.mock('../../../../common/index_controls', () => ({ + firstFieldOption: jest.fn(), + getFields: jest.fn(), + getIndexOptions: jest.fn(), + getIndexPatterns: jest.fn(), +})); + +describe('IndexActionConnectorFields renders', () => { + test('all connector fields is rendered', async () => { + const mocks = coreMock.createSetup(); + + const { getIndexPatterns } = jest.requireMock('../../../../common/index_controls'); + getIndexPatterns.mockResolvedValueOnce([ + { + id: 'indexPattern1', + attributes: { + title: 'indexPattern1', + }, + }, + { + id: 'indexPattern2', + attributes: { + title: 'indexPattern2', + }, + }, + ]); + const { getFields } = jest.requireMock('../../../../common/index_controls'); + getFields.mockResolvedValueOnce([ + { + type: 'date', + name: 'test1', + }, + { + type: 'text', + name: 'test2', + }, + ]); + + const actionConnector = { + secrets: {}, + id: 'test', + actionTypeId: '.index', + name: 'es_index', + config: { + index: 'test', + refresh: false, + executionTimeField: 'test1', + }, + } as EsIndexActionConnector; + const wrapper = mountWithIntl( + {}} + editActionSecrets={() => {}} + http={mocks.http} + /> + ); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect(wrapper.find('[data-test-subj="connectorIndexesComboBox"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="indexRefreshCheckbox"]').length > 0).toBeTruthy(); + + const indexSearchBoxValue = wrapper.find('[data-test-subj="comboBoxSearchInput"]'); + expect(indexSearchBoxValue.first().props().value).toEqual(''); + + const indexComboBox = wrapper.find('#indexConnectorSelectSearchBox'); + indexComboBox.first().simulate('click'); + const event = { target: { value: 'indexPattern1' } }; + indexComboBox + .find('input') + .first() + .simulate('change', event); + + const indexSearchBoxValueBeforeEnterData = wrapper.find( + '[data-test-subj="comboBoxSearchInput"]' + ); + expect(indexSearchBoxValueBeforeEnterData.first().props().value).toEqual('indexPattern1'); + + const indexComboBoxClear = wrapper.find('[data-test-subj="comboBoxClearButton"]'); + indexComboBoxClear.first().simulate('click'); + + const indexSearchBoxValueAfterEnterData = wrapper.find( + '[data-test-subj="comboBoxSearchInput"]' + ); + expect(indexSearchBoxValueAfterEnterData.first().props().value).toEqual('indexPattern1'); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.tsx similarity index 64% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.tsx rename to x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.tsx index 55a219ca94aead..c436b7611680be 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.tsx @@ -3,12 +3,11 @@ * 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, useState, useEffect } from 'react'; +import React, { useState, useEffect } from 'react'; import { EuiFormRow, EuiSwitch, EuiSpacer, - EuiCodeEditor, EuiComboBox, EuiComboBoxOptionOption, EuiSelect, @@ -17,68 +16,22 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { useXJsonMode } from '../../../../../../../src/plugins/es_ui_shared/static/ace_x_json/hooks'; -import { - ActionTypeModel, - ActionConnectorFieldsProps, - ValidationResult, - ActionParamsProps, -} from '../../../types'; -import { IndexActionParams, EsIndexActionConnector } from './types'; -import { getTimeFieldOptions } from '../../../common/lib/get_time_options'; +import { ActionConnectorFieldsProps } from '../../../../types'; +import { EsIndexActionConnector } from '.././types'; +import { getTimeFieldOptions } from '../../../../common/lib/get_time_options'; import { firstFieldOption, getFields, getIndexOptions, getIndexPatterns, -} from '../../../common/index_controls'; -import { AddMessageVariables } from '../add_message_variables'; - -export function getActionType(): ActionTypeModel { - return { - id: '.index', - iconClass: 'indexOpen', - selectMessage: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.selectMessageText', - { - defaultMessage: 'Index data into Elasticsearch.', - } - ), - actionTypeTitle: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.actionTypeTitle', - { - defaultMessage: 'Index data', - } - ), - validateConnector: (action: EsIndexActionConnector): ValidationResult => { - const validationResult = { errors: {} }; - const errors = { - index: new Array(), - }; - validationResult.errors = errors; - if (!action.config.index) { - errors.index.push( - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.error.requiredIndexText', - { - defaultMessage: 'Index is required.', - } - ) - ); - } - return validationResult; - }, - actionConnectorFields: IndexActionConnectorFields, - actionParamsFields: IndexParamsFields, - validateParams: (): ValidationResult => { - return { errors: {} }; - }, - }; -} +} from '../../../../common/index_controls'; -const IndexActionConnectorFields: React.FunctionComponent> = ({ action, editActionConfig, errors, http }) => { +export const IndexActionConnectorFields = ({ + action, + editActionConfig, + errors, + http, +}: ActionConnectorFieldsProps) => { const { index, refresh, executionTimeField } = action.config; const [hasTimeFieldCheckbox, setTimeFieldCheckboxState] = useState( executionTimeField != null @@ -269,74 +222,11 @@ const IndexActionConnectorFields: React.FunctionComponent> = ({ - actionParams, - index, - editAction, - messageVariables, -}) => { - const { documents } = actionParams; - const { xJsonMode, convertToJson, setXJson, xJson } = useXJsonMode( - documents && documents.length > 0 ? documents[0] : null - ); - const onSelectMessageVariable = (variable: string) => { - const value = (xJson ?? '').concat(` {{${variable}}}`); - setXJson(value); - // Keep the documents in sync with the editor content - onDocumentsChange(convertToJson(value)); - }; - - function onDocumentsChange(updatedDocuments: string) { - try { - const documentsJSON = JSON.parse(updatedDocuments); - editAction('documents', [documentsJSON], index); - // eslint-disable-next-line no-empty - } catch (e) {} - } - return ( - - onSelectMessageVariable(variable)} - paramsProperty="documents" - /> - } - > - { - setXJson(xjson); - // Keep the documents in sync with the editor content - onDocumentsChange(convertToJson(xjson)); - }} - /> - - - ); -}; - // if the string == null or is empty, return null, else return string function nullableString(str: string | null | undefined) { if (str == null || str.trim() === '') return null; return str; } + +// eslint-disable-next-line import/no-default-export +export { IndexActionConnectorFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.test.tsx new file mode 100644 index 00000000000000..5f05a56a228e2b --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.test.tsx @@ -0,0 +1,33 @@ +/* + * 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 from 'react'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import ParamsFields from './es_index_params'; + +describe('IndexParamsFields renders', () => { + test('all params fields is rendered', () => { + const actionParams = { + documents: [{ test: 123 }], + }; + const wrapper = mountWithIntl( + {}} + index={0} + /> + ); + expect( + wrapper + .find('[data-test-subj="actionIndexDoc"]') + .first() + .prop('value') + ).toBe(`{ + "test": 123 +}`); + expect(wrapper.find('[data-test-subj="documentsAddVariableButton"]').length > 0).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.tsx new file mode 100644 index 00000000000000..0b095cdc269847 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.tsx @@ -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 { EuiFormRow, EuiCodeEditor } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { useXJsonMode } from '../../../../../../../../src/plugins/es_ui_shared/static/ace_x_json/hooks'; +import { ActionParamsProps } from '../../../../types'; +import { IndexActionParams } from '.././types'; +import { AddMessageVariables } from '../../add_message_variables'; + +export const IndexParamsFields = ({ + actionParams, + index, + editAction, + messageVariables, +}: ActionParamsProps) => { + const { documents } = actionParams; + const { xJsonMode, convertToJson, setXJson, xJson } = useXJsonMode( + documents && documents.length > 0 ? documents[0] : null + ); + const onSelectMessageVariable = (variable: string) => { + const value = (xJson ?? '').concat(` {{${variable}}}`); + setXJson(value); + // Keep the documents in sync with the editor content + onDocumentsChange(convertToJson(value)); + }; + + function onDocumentsChange(updatedDocuments: string) { + try { + const documentsJSON = JSON.parse(updatedDocuments); + editAction('documents', [documentsJSON], index); + // eslint-disable-next-line no-empty + } catch (e) {} + } + return ( + + onSelectMessageVariable(variable)} + paramsProperty="documents" + /> + } + > + { + setXJson(xjson); + // Keep the documents in sync with the editor content + onDocumentsChange(convertToJson(xjson)); + }} + /> + + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { IndexParamsFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts index 6ffd9b2c9ffde8..8d82b35f3fb7c8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getActionType as getServerLogActionType } from './server_log'; -import { getActionType as getSlackActionType } from './slack'; -import { getActionType as getEmailActionType } from './email'; -import { getActionType as getIndexActionType } from './es_index'; -import { getActionType as getPagerDutyActionType } from './pagerduty'; -import { getActionType as getWebhookActionType } from './webhook'; +import { getActionType as getServerLogActionType } from './server_log/server_log'; +import { getActionType as getSlackActionType } from './slack/slack'; +import { getActionType as getEmailActionType } from './email/email'; +import { getActionType as getIndexActionType } from './es_index/es_index'; +import { getActionType as getPagerDutyActionType } from './pagerduty/pagerduty'; +import { getActionType as getWebhookActionType } from './webhook/webhook'; import { TypeRegistry } from '../../type_registry'; import { ActionTypeModel } from '../../../types'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.test.tsx deleted file mode 100644 index 1c9e87310107fc..00000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.test.tsx +++ /dev/null @@ -1,189 +0,0 @@ -/* - * 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, { FunctionComponent } from 'react'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { TypeRegistry } from '../../type_registry'; -import { registerBuiltInActionTypes } from './index'; -import { ActionTypeModel, ActionParamsProps } from '../../../types'; -import { - PagerDutyActionParams, - EventActionOptions, - SeverityActionOptions, - PagerDutyActionConnector, -} from './types'; - -const ACTION_TYPE_ID = '.pagerduty'; -let actionTypeModel: ActionTypeModel; - -beforeAll(() => { - const actionTypeRegistry = new TypeRegistry(); - registerBuiltInActionTypes({ actionTypeRegistry }); - const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); - if (getResult !== null) { - actionTypeModel = getResult; - } -}); - -describe('actionTypeRegistry.get() works', () => { - test('action type static data is as expected', () => { - expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); - expect(actionTypeModel.iconClass).toEqual('test-file-stub'); - }); -}); - -describe('pagerduty connector validation', () => { - test('connector validation succeeds when connector config is valid', () => { - const actionConnector = { - secrets: { - routingKey: 'test', - }, - id: 'test', - actionTypeId: '.pagerduty', - name: 'pagerduty', - config: { - apiUrl: 'http:\\test', - }, - } as PagerDutyActionConnector; - - expect(actionTypeModel.validateConnector(actionConnector)).toEqual({ - errors: { - routingKey: [], - }, - }); - - delete actionConnector.config.apiUrl; - actionConnector.secrets.routingKey = 'test1'; - expect(actionTypeModel.validateConnector(actionConnector)).toEqual({ - errors: { - routingKey: [], - }, - }); - }); - - test('connector validation fails when connector config is not valid', () => { - const actionConnector = { - secrets: {}, - id: 'test', - actionTypeId: '.pagerduty', - name: 'pagerduty', - config: { - apiUrl: 'http:\\test', - }, - } as PagerDutyActionConnector; - - expect(actionTypeModel.validateConnector(actionConnector)).toEqual({ - errors: { - routingKey: ['A routing key is required.'], - }, - }); - }); -}); - -describe('pagerduty action params validation', () => { - test('action params validation succeeds when action params is valid', () => { - const actionParams = { - eventAction: 'trigger', - dedupKey: 'test', - summary: '2323', - source: 'source', - severity: 'critical', - timestamp: new Date().toISOString(), - component: 'test', - group: 'group', - class: 'test class', - }; - - expect(actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { - summary: [], - timestamp: [], - }, - }); - }); -}); - -describe('PagerDutyActionConnectorFields renders', () => { - test('all connector fields is rendered', () => { - expect(actionTypeModel.actionConnectorFields).not.toBeNull(); - if (!actionTypeModel.actionConnectorFields) { - return; - } - const ConnectorFields = actionTypeModel.actionConnectorFields; - const actionConnector = { - secrets: { - routingKey: 'test', - }, - id: 'test', - actionTypeId: '.pagerduty', - name: 'pagerduty', - config: { - apiUrl: 'http:\\test', - }, - } as PagerDutyActionConnector; - const wrapper = mountWithIntl( - {}} - editActionSecrets={() => {}} - /> - ); - expect(wrapper.find('[data-test-subj="pagerdutyApiUrlInput"]').length > 0).toBeTruthy(); - expect( - wrapper - .find('[data-test-subj="pagerdutyApiUrlInput"]') - .first() - .prop('value') - ).toBe('http:\\test'); - expect(wrapper.find('[data-test-subj="pagerdutyRoutingKeyInput"]').length > 0).toBeTruthy(); - }); -}); - -describe('PagerDutyParamsFields renders', () => { - test('all params fields is rendered', () => { - expect(actionTypeModel.actionParamsFields).not.toBeNull(); - if (!actionTypeModel.actionParamsFields) { - return; - } - const ParamsFields = actionTypeModel.actionParamsFields as FunctionComponent< - ActionParamsProps - >; - const actionParams = { - eventAction: EventActionOptions.TRIGGER, - dedupKey: 'test', - summary: '2323', - source: 'source', - severity: SeverityActionOptions.CRITICAL, - timestamp: new Date().toISOString(), - component: 'test', - group: 'group', - class: 'test class', - }; - const wrapper = mountWithIntl( - {}} - index={0} - /> - ); - expect(wrapper.find('[data-test-subj="severitySelect"]').length > 0).toBeTruthy(); - expect( - wrapper - .find('[data-test-subj="severitySelect"]') - .first() - .prop('value') - ).toStrictEqual('critical'); - expect(wrapper.find('[data-test-subj="eventActionSelect"]').length > 0).toBeTruthy(); - expect(wrapper.find('[data-test-subj="dedupKeyInput"]').length > 0).toBeTruthy(); - expect(wrapper.find('[data-test-subj="timestampInput"]').length > 0).toBeTruthy(); - expect(wrapper.find('[data-test-subj="componentInput"]').length > 0).toBeTruthy(); - expect(wrapper.find('[data-test-subj="groupInput"]').length > 0).toBeTruthy(); - expect(wrapper.find('[data-test-subj="sourceInput"]').length > 0).toBeTruthy(); - expect(wrapper.find('[data-test-subj="pagerdutySummaryInput"]').length > 0).toBeTruthy(); - expect(wrapper.find('[data-test-subj="dedupKeyAddVariableButton"]').length > 0).toBeTruthy(); - }); -}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.svg b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.svg similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.svg rename to x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.svg diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.test.tsx new file mode 100644 index 00000000000000..d9aa72d5932cb6 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.test.tsx @@ -0,0 +1,106 @@ +/* + * 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, { FunctionComponent } from 'react'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { TypeRegistry } from '../../../type_registry'; +import { registerBuiltInActionTypes } from '.././index'; +import { ActionTypeModel, ActionParamsProps } from '../../../../types'; +import { + PagerDutyActionParams, + EventActionOptions, + SeverityActionOptions, + PagerDutyActionConnector, +} from '.././types'; + +const ACTION_TYPE_ID = '.pagerduty'; +let actionTypeModel: ActionTypeModel; + +beforeAll(() => { + const actionTypeRegistry = new TypeRegistry(); + registerBuiltInActionTypes({ actionTypeRegistry }); + const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); + if (getResult !== null) { + actionTypeModel = getResult; + } +}); + +describe('actionTypeRegistry.get() works', () => { + test('action type static data is as expected', () => { + expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); + expect(actionTypeModel.iconClass).toEqual('test-file-stub'); + }); +}); + +describe('pagerduty connector validation', () => { + test('connector validation succeeds when connector config is valid', () => { + const actionConnector = { + secrets: { + routingKey: 'test', + }, + id: 'test', + actionTypeId: '.pagerduty', + name: 'pagerduty', + config: { + apiUrl: 'http:\\test', + }, + } as PagerDutyActionConnector; + + expect(actionTypeModel.validateConnector(actionConnector)).toEqual({ + errors: { + routingKey: [], + }, + }); + + delete actionConnector.config.apiUrl; + actionConnector.secrets.routingKey = 'test1'; + expect(actionTypeModel.validateConnector(actionConnector)).toEqual({ + errors: { + routingKey: [], + }, + }); + }); + + test('connector validation fails when connector config is not valid', () => { + const actionConnector = { + secrets: {}, + id: 'test', + actionTypeId: '.pagerduty', + name: 'pagerduty', + config: { + apiUrl: 'http:\\test', + }, + } as PagerDutyActionConnector; + + expect(actionTypeModel.validateConnector(actionConnector)).toEqual({ + errors: { + routingKey: ['A routing key is required.'], + }, + }); + }); +}); + +describe('pagerduty action params validation', () => { + test('action params validation succeeds when action params is valid', () => { + const actionParams = { + eventAction: 'trigger', + dedupKey: 'test', + summary: '2323', + source: 'source', + severity: 'critical', + timestamp: new Date().toISOString(), + component: 'test', + group: 'group', + class: 'test class', + }; + + expect(actionTypeModel.validateParams(actionParams)).toEqual({ + errors: { + summary: [], + timestamp: [], + }, + }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx new file mode 100644 index 00000000000000..6f214525de259c --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx @@ -0,0 +1,108 @@ +/* + * 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, { lazy, Suspense } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import moment from 'moment'; +import { ActionTypeModel, ValidationResult } from '../../../../types'; +import { PagerDutyActionParams, PagerDutyActionConnector } from '.././types'; +import pagerDutySvg from './pagerduty.svg'; +import { hasMustacheTokens } from '../../../lib/has_mustache_tokens'; + +const PagerDutyActionConnectorFields = lazy(() => import('./pagerduty_connectors')); +const PagerDutyParamsFields = lazy(() => import('./pagerduty_params')); + +export function getActionType(): ActionTypeModel { + return { + id: '.pagerduty', + iconClass: pagerDutySvg, + selectMessage: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.selectMessageText', + { + defaultMessage: 'Send an event in PagerDuty.', + } + ), + actionTypeTitle: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.actionTypeTitle', + { + defaultMessage: 'Send to PagerDuty', + } + ), + validateConnector: (action: PagerDutyActionConnector): ValidationResult => { + const validationResult = { errors: {} }; + const errors = { + routingKey: new Array(), + }; + validationResult.errors = errors; + if (!action.secrets.routingKey) { + errors.routingKey.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.requiredRoutingKeyText', + { + defaultMessage: 'A routing key is required.', + } + ) + ); + } + return validationResult; + }, + validateParams: (actionParams: PagerDutyActionParams): ValidationResult => { + const validationResult = { errors: {} }; + const errors = { + summary: new Array(), + timestamp: new Array(), + }; + validationResult.errors = errors; + if (!actionParams.summary?.length) { + errors.summary.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.requiredSummaryText', + { + defaultMessage: 'Summary is required.', + } + ) + ); + } + if (actionParams.timestamp && !hasMustacheTokens(actionParams.timestamp)) { + if (isNaN(Date.parse(actionParams.timestamp))) { + const { nowShortFormat, nowLongFormat } = getValidTimestampExamples(); + errors.timestamp.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.invalidTimestamp', + { + defaultMessage: + 'Timestamp must be a valid date, such as {nowShortFormat} or {nowLongFormat}.', + values: { + nowShortFormat, + nowLongFormat, + }, + } + ) + ); + } + } + return validationResult; + }, + actionConnectorFields: (props: any) => ( + }> + + + ), + actionParamsFields: (props: any) => ( + }> + + + ), + }; +} + +function getValidTimestampExamples() { + const now = moment(); + return { + nowShortFormat: now.format('YYYY-MM-DD'), + nowLongFormat: now.format('YYYY-MM-DD h:mm:ss'), + }; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.test.tsx new file mode 100644 index 00000000000000..03c2892df0107e --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.test.tsx @@ -0,0 +1,41 @@ +/* + * 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 from 'react'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { PagerDutyActionConnector } from '.././types'; +import PagerDutyActionConnectorFields from './pagerduty_connectors'; + +describe('PagerDutyActionConnectorFields renders', () => { + test('all connector fields is rendered', () => { + const actionConnector = { + secrets: { + routingKey: 'test', + }, + id: 'test', + actionTypeId: '.pagerduty', + name: 'pagerduty', + config: { + apiUrl: 'http:\\test', + }, + } as PagerDutyActionConnector; + const wrapper = mountWithIntl( + {}} + editActionSecrets={() => {}} + /> + ); + expect(wrapper.find('[data-test-subj="pagerdutyApiUrlInput"]').length > 0).toBeTruthy(); + expect( + wrapper + .find('[data-test-subj="pagerdutyApiUrlInput"]') + .first() + .prop('value') + ).toBe('http:\\test'); + expect(wrapper.find('[data-test-subj="pagerdutyRoutingKeyInput"]').length > 0).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx similarity index 83% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.tsx rename to x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx index 15f91ae1d46094..83bf8c5488f3d7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx @@ -20,87 +20,11 @@ import { ActionConnectorFieldsProps, ValidationResult, ActionParamsProps, -} from '../../../types'; -import { PagerDutyActionParams, PagerDutyActionConnector } from './types'; -import pagerDutySvg from './pagerduty.svg'; -import { AddMessageVariables } from '../add_message_variables'; -import { hasMustacheTokens } from '../../lib/has_mustache_tokens'; - -export function getActionType(): ActionTypeModel { - return { - id: '.pagerduty', - iconClass: pagerDutySvg, - selectMessage: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.selectMessageText', - { - defaultMessage: 'Send an event in PagerDuty.', - } - ), - actionTypeTitle: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.actionTypeTitle', - { - defaultMessage: 'Send to PagerDuty', - } - ), - validateConnector: (action: PagerDutyActionConnector): ValidationResult => { - const validationResult = { errors: {} }; - const errors = { - routingKey: new Array(), - }; - validationResult.errors = errors; - if (!action.secrets.routingKey) { - errors.routingKey.push( - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.requiredRoutingKeyText', - { - defaultMessage: 'A routing key is required.', - } - ) - ); - } - return validationResult; - }, - validateParams: (actionParams: PagerDutyActionParams): ValidationResult => { - const validationResult = { errors: {} }; - const errors = { - summary: new Array(), - timestamp: new Array(), - }; - validationResult.errors = errors; - if (!actionParams.summary?.length) { - errors.summary.push( - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.requiredSummaryText', - { - defaultMessage: 'Summary is required.', - } - ) - ); - } - if (actionParams.timestamp && !hasMustacheTokens(actionParams.timestamp)) { - if (isNaN(Date.parse(actionParams.timestamp))) { - const { nowShortFormat, nowLongFormat } = getValidTimestampExamples(); - errors.timestamp.push( - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.invalidTimestamp', - { - defaultMessage: - 'Timestamp must be a valid date, such as {nowShortFormat} or {nowLongFormat}.', - values: { - nowShortFormat, - nowLongFormat, - }, - } - ) - ); - } - } - return validationResult; - }, - actionConnectorFields: PagerDutyActionConnectorFields, - actionParamsFields: PagerDutyParamsFields, - }; -} +} from '../../../../types'; +import { PagerDutyActionParams, PagerDutyActionConnector } from '.././types'; +import pagerDutySvg from '.././pagerduty.svg'; +import { AddMessageVariables } from '../../add_message_variables'; +import { hasMustacheTokens } from '../../../lib/has_mustache_tokens'; const PagerDutyActionConnectorFields: React.FunctionComponent { + test('all params fields is rendered', () => { + const actionParams = { + eventAction: EventActionOptions.TRIGGER, + dedupKey: 'test', + summary: '2323', + source: 'source', + severity: SeverityActionOptions.CRITICAL, + timestamp: new Date().toISOString(), + component: 'test', + group: 'group', + class: 'test class', + }; + const wrapper = mountWithIntl( + {}} + index={0} + /> + ); + expect(wrapper.find('[data-test-subj="severitySelect"]').length > 0).toBeTruthy(); + expect( + wrapper + .find('[data-test-subj="severitySelect"]') + .first() + .prop('value') + ).toStrictEqual('critical'); + expect(wrapper.find('[data-test-subj="eventActionSelect"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="dedupKeyInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="timestampInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="componentInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="groupInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="sourceInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="pagerdutySummaryInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="dedupKeyAddVariableButton"]').length > 0).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx new file mode 100644 index 00000000000000..a017c1e26b07da --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx @@ -0,0 +1,414 @@ +/* + * 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 { + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiSelect, + EuiLink, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import moment from 'moment'; +import { + ActionTypeModel, + ActionConnectorFieldsProps, + ValidationResult, + ActionParamsProps, +} from '../../../../types'; +import { PagerDutyActionParams, PagerDutyActionConnector } from '.././types'; +import pagerDutySvg from '.././pagerduty.svg'; +import { AddMessageVariables } from '../../add_message_variables'; +import { hasMustacheTokens } from '../../../lib/has_mustache_tokens'; + +const PagerDutyParamsFields: React.FunctionComponent> = ({ + actionParams, + editAction, + index, + messageVariables, + errors, +}) => { + const { + eventAction, + dedupKey, + summary, + source, + severity, + timestamp, + component, + group, + } = actionParams; + const severityOptions = [ + { + value: 'info', + text: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectInfoOptionLabel', + { + defaultMessage: 'Info', + } + ), + }, + { + value: 'critical', + text: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectCriticalOptionLabel', + { + defaultMessage: 'Critical', + } + ), + }, + { + value: 'warning', + text: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectWarningOptionLabel', + { + defaultMessage: 'Warning', + } + ), + }, + { + value: 'error', + text: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectErrorOptionLabel', + { + defaultMessage: 'Error', + } + ), + }, + ]; + const eventActionOptions = [ + { + value: 'trigger', + text: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectTriggerOptionLabel', + { + defaultMessage: 'Trigger', + } + ), + }, + { + value: 'resolve', + text: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectResolveOptionLabel', + { + defaultMessage: 'Resolve', + } + ), + }, + { + value: 'acknowledge', + text: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectAcknowledgeOptionLabel', + { + defaultMessage: 'Acknowledge', + } + ), + }, + ]; + + const onSelectMessageVariable = (paramsProperty: string, variable: string) => { + editAction( + paramsProperty, + ((actionParams as any)[paramsProperty] ?? '').concat(` {{${variable}}}`), + index + ); + }; + + return ( + + + + + { + editAction('severity', e.target.value, index); + }} + /> + + + + + { + editAction('eventAction', e.target.value, index); + }} + /> + + + + + + + onSelectMessageVariable('dedupKey', variable) + } + paramsProperty="dedupKey" + /> + } + > + ) => { + editAction('dedupKey', e.target.value, index); + }} + onBlur={() => { + if (!dedupKey) { + editAction('dedupKey', '', index); + } + }} + /> + + + + 0 && timestamp !== undefined} + label={i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.timestampTextFieldLabel', + { + defaultMessage: 'Timestamp (optional)', + } + )} + labelAppend={ + + onSelectMessageVariable('timestamp', variable) + } + paramsProperty="timestamp" + /> + } + > + 0 && timestamp !== undefined} + onChange={(e: React.ChangeEvent) => { + editAction('timestamp', e.target.value, index); + }} + onBlur={() => { + if (timestamp?.trim()) { + editAction('timestamp', timestamp.trim(), index); + } else { + editAction('timestamp', '', index); + } + }} + /> + + + + + onSelectMessageVariable('component', variable) + } + paramsProperty="component" + /> + } + > + ) => { + editAction('component', e.target.value, index); + }} + onBlur={() => { + if (!component) { + editAction('component', '', index); + } + }} + /> + + onSelectMessageVariable('group', variable)} + paramsProperty="group" + /> + } + > + ) => { + editAction('group', e.target.value, index); + }} + onBlur={() => { + if (!group) { + editAction('group', '', index); + } + }} + /> + + onSelectMessageVariable('source', variable)} + paramsProperty="source" + /> + } + > + ) => { + editAction('source', e.target.value, index); + }} + onBlur={() => { + if (!source) { + editAction('source', '', index); + } + }} + /> + + 0 && summary !== undefined} + label={i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.summaryFieldLabel', + { + defaultMessage: 'Summary', + } + )} + labelAppend={ + + onSelectMessageVariable('summary', variable) + } + paramsProperty="summary" + /> + } + > + 0 && summary !== undefined} + name="summary" + value={summary || ''} + data-test-subj="pagerdutySummaryInput" + onChange={(e: React.ChangeEvent) => { + editAction('summary', e.target.value, index); + }} + onBlur={() => { + if (!summary) { + editAction('summary', '', index); + } + }} + /> + + onSelectMessageVariable('class', variable)} + paramsProperty="class" + /> + } + > + ) => { + editAction('class', e.target.value, index); + }} + onBlur={() => { + if (!actionParams.class) { + editAction('class', '', index); + } + }} + /> + + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { PagerDutyParamsFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log.test.tsx deleted file mode 100644 index fce8f25e511314..00000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log.test.tsx +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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, { FunctionComponent } from 'react'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { TypeRegistry } from '../../type_registry'; -import { registerBuiltInActionTypes } from './index'; -import { ActionTypeModel, ActionConnector, ActionParamsProps } from '../../../types'; -import { ServerLogActionParams, ServerLogLevelOptions } from './types'; - -const ACTION_TYPE_ID = '.server-log'; -let actionTypeModel: ActionTypeModel; - -beforeAll(() => { - const actionTypeRegistry = new TypeRegistry(); - registerBuiltInActionTypes({ actionTypeRegistry }); - const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); - if (getResult !== null) { - actionTypeModel = getResult; - } -}); - -describe('actionTypeRegistry.get() works', () => { - test('action type static data is as expected', () => { - expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); - expect(actionTypeModel.iconClass).toEqual('logsApp'); - }); -}); - -describe('server-log connector validation', () => { - test('connector validation succeeds when connector config is valid', () => { - const actionConnector = { - secrets: {}, - id: 'test', - actionTypeId: '.server-log', - name: 'server-log', - config: {}, - } as ActionConnector; - - expect(actionTypeModel.validateConnector(actionConnector)).toEqual({ - errors: {}, - }); - }); -}); - -describe('action params validation', () => { - test('action params validation succeeds when action params is valid', () => { - const actionParams = { - message: 'test message', - level: 'trace', - }; - - expect(actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { message: [] }, - }); - }); -}); - -describe('ServerLogParamsFields renders', () => { - test('all params fields is rendered', () => { - expect(actionTypeModel.actionParamsFields).not.toBeNull(); - if (!actionTypeModel.actionParamsFields) { - return; - } - const ParamsFields = actionTypeModel.actionParamsFields as FunctionComponent< - ActionParamsProps - >; - const actionParams = { - level: ServerLogLevelOptions.TRACE, - message: 'test', - }; - const wrapper = mountWithIntl( - {}} - index={0} - defaultMessage={'test default message'} - /> - ); - expect(wrapper.find('[data-test-subj="loggingLevelSelect"]').length > 0).toBeTruthy(); - expect( - wrapper - .find('[data-test-subj="loggingLevelSelect"]') - .first() - .prop('value') - ).toStrictEqual('trace'); - expect(wrapper.find('[data-test-subj="loggingMessageInput"]').length > 0).toBeTruthy(); - }); - - test('level param field is rendered with default value if not selected', () => { - expect(actionTypeModel.actionParamsFields).not.toBeNull(); - if (!actionTypeModel.actionParamsFields) { - return; - } - const ParamsFields = actionTypeModel.actionParamsFields as FunctionComponent< - ActionParamsProps - >; - const actionParams = { - message: 'test message', - level: ServerLogLevelOptions.INFO, - }; - const wrapper = mountWithIntl( - {}} - index={0} - /> - ); - expect(wrapper.find('[data-test-subj="loggingLevelSelect"]').length > 0).toBeTruthy(); - expect( - wrapper - .find('[data-test-subj="loggingLevelSelect"]') - .first() - .prop('value') - ).toStrictEqual('info'); - expect(wrapper.find('[data-test-subj="loggingMessageInput"]').length > 0).toBeTruthy(); - }); - - test('params validation fails when message is not valid', () => { - const actionParams = { - message: '', - }; - - expect(actionTypeModel.validateParams(actionParams)).toEqual({ - errors: { - message: ['Message is required.'], - }, - }); - }); -}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx new file mode 100644 index 00000000000000..39fb4cdc10904f --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx @@ -0,0 +1,71 @@ +/* + * 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, { FunctionComponent } from 'react'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { TypeRegistry } from '../../../type_registry'; +import { registerBuiltInActionTypes } from '.././index'; +import { ActionTypeModel, ActionConnector, ActionParamsProps } from '../../../../types'; +import { ServerLogActionParams, ServerLogLevelOptions } from '../types'; + +const ACTION_TYPE_ID = '.server-log'; +let actionTypeModel: ActionTypeModel; + +beforeAll(() => { + const actionTypeRegistry = new TypeRegistry(); + registerBuiltInActionTypes({ actionTypeRegistry }); + const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); + if (getResult !== null) { + actionTypeModel = getResult; + } +}); + +describe('actionTypeRegistry.get() works', () => { + test('action type static data is as expected', () => { + expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); + expect(actionTypeModel.iconClass).toEqual('logsApp'); + }); +}); + +describe('server-log connector validation', () => { + test('connector validation succeeds when connector config is valid', () => { + const actionConnector = { + secrets: {}, + id: 'test', + actionTypeId: '.server-log', + name: 'server-log', + config: {}, + } as ActionConnector; + + expect(actionTypeModel.validateConnector(actionConnector)).toEqual({ + errors: {}, + }); + }); +}); + +describe('action params validation', () => { + test('action params validation succeeds when action params is valid', () => { + const actionParams = { + message: 'test message', + level: 'trace', + }; + + expect(actionTypeModel.validateParams(actionParams)).toEqual({ + errors: { message: [] }, + }); + }); + + test('params validation fails when message is not valid', () => { + const actionParams = { + message: '', + }; + + expect(actionTypeModel.validateParams(actionParams)).toEqual({ + errors: { + message: ['Message is required.'], + }, + }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.tsx new file mode 100644 index 00000000000000..9de670fefbccd1 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.tsx @@ -0,0 +1,58 @@ +/* + * 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, { lazy, Suspense } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import { ActionTypeModel, ValidationResult } from '../../../../types'; +import { ServerLogActionParams } from '../types'; + +const ServerLogParamsFields = lazy(() => import('./server_log_params')); + +export function getActionType(): ActionTypeModel { + return { + id: '.server-log', + iconClass: 'logsApp', + selectMessage: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.selectMessageText', + { + defaultMessage: 'Add a message to a Kibana log.', + } + ), + actionTypeTitle: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.actionTypeTitle', + { + defaultMessage: 'Send to Server log', + } + ), + validateConnector: (): ValidationResult => { + return { errors: {} }; + }, + validateParams: (actionParams: ServerLogActionParams): ValidationResult => { + const validationResult = { errors: {} }; + const errors = { + message: new Array(), + }; + validationResult.errors = errors; + if (!actionParams.message?.length) { + errors.message.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredServerLogMessageText', + { + defaultMessage: 'Message is required.', + } + ) + ); + } + return validationResult; + }, + actionConnectorFields: null, + actionParamsFields: (props: any) => ( + }> + + + ), + }; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.test.tsx new file mode 100644 index 00000000000000..d2e1d1e4500bc6 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.test.tsx @@ -0,0 +1,58 @@ +/* + * 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 from 'react'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { ServerLogLevelOptions } from '.././types'; +import ServerLogParamsFields from './server_log_params'; + +describe('ServerLogParamsFields renders', () => { + test('all params fields is rendered', () => { + const actionParams = { + level: ServerLogLevelOptions.TRACE, + message: 'test', + }; + const wrapper = mountWithIntl( + {}} + index={0} + defaultMessage={'test default message'} + /> + ); + expect(wrapper.find('[data-test-subj="loggingLevelSelect"]').length > 0).toBeTruthy(); + expect( + wrapper + .find('[data-test-subj="loggingLevelSelect"]') + .first() + .prop('value') + ).toStrictEqual('trace'); + expect(wrapper.find('[data-test-subj="loggingMessageInput"]').length > 0).toBeTruthy(); + }); + + test('level param field is rendered with default value if not selected', () => { + const actionParams = { + message: 'test message', + level: ServerLogLevelOptions.INFO, + }; + const wrapper = mountWithIntl( + {}} + index={0} + /> + ); + expect(wrapper.find('[data-test-subj="loggingLevelSelect"]').length > 0).toBeTruthy(); + expect( + wrapper + .find('[data-test-subj="loggingLevelSelect"]') + .first() + .prop('value') + ).toStrictEqual('info'); + expect(wrapper.find('[data-test-subj="loggingMessageInput"]').length > 0).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.tsx similarity index 67% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log.tsx rename to x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.tsx index a4c83ce76f04e8..64d39e238be760 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.tsx @@ -6,51 +6,9 @@ import React, { Fragment, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiSelect, EuiTextArea, EuiFormRow } from '@elastic/eui'; -import { ActionTypeModel, ValidationResult, ActionParamsProps } from '../../../types'; -import { ServerLogActionParams } from './types'; -import { AddMessageVariables } from '../add_message_variables'; - -export function getActionType(): ActionTypeModel { - return { - id: '.server-log', - iconClass: 'logsApp', - selectMessage: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.selectMessageText', - { - defaultMessage: 'Add a message to a Kibana log.', - } - ), - actionTypeTitle: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.actionTypeTitle', - { - defaultMessage: 'Send to Server log', - } - ), - validateConnector: (): ValidationResult => { - return { errors: {} }; - }, - validateParams: (actionParams: ServerLogActionParams): ValidationResult => { - const validationResult = { errors: {} }; - const errors = { - message: new Array(), - }; - validationResult.errors = errors; - if (!actionParams.message?.length) { - errors.message.push( - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredServerLogMessageText', - { - defaultMessage: 'Message is required.', - } - ) - ); - } - return validationResult; - }, - actionConnectorFields: null, - actionParamsFields: ServerLogParamsFields, - }; -} +import { ActionParamsProps } from '../../../../types'; +import { ServerLogActionParams } from '.././types'; +import { AddMessageVariables } from '../../add_message_variables'; export const ServerLogParamsFields: React.FunctionComponent ); }; + +// eslint-disable-next-line import/no-default-export +export { ServerLogParamsFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack.tsx deleted file mode 100644 index fd066e172bbfea..00000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack.tsx +++ /dev/null @@ -1,188 +0,0 @@ -/* - * 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, useEffect } from 'react'; -import { EuiFieldText, EuiTextArea, EuiFormRow, EuiLink } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { - ActionTypeModel, - ActionConnectorFieldsProps, - ValidationResult, - ActionParamsProps, -} from '../../../types'; -import { SlackActionParams, SlackActionConnector } from './types'; -import { AddMessageVariables } from '../add_message_variables'; - -export function getActionType(): ActionTypeModel { - return { - id: '.slack', - iconClass: 'logoSlack', - selectMessage: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.selectMessageText', - { - defaultMessage: 'Send a message to a Slack channel or user.', - } - ), - actionTypeTitle: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.actionTypeTitle', - { - defaultMessage: 'Send to Slack', - } - ), - validateConnector: (action: SlackActionConnector): ValidationResult => { - const validationResult = { errors: {} }; - const errors = { - webhookUrl: new Array(), - }; - validationResult.errors = errors; - if (!action.secrets.webhookUrl) { - errors.webhookUrl.push( - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.requiredWebhookUrlText', - { - defaultMessage: 'Webhook URL is required.', - } - ) - ); - } - return validationResult; - }, - validateParams: (actionParams: SlackActionParams): ValidationResult => { - const validationResult = { errors: {} }; - const errors = { - message: new Array(), - }; - validationResult.errors = errors; - if (!actionParams.message?.length) { - errors.message.push( - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSlackMessageText', - { - defaultMessage: 'Message is required.', - } - ) - ); - } - return validationResult; - }, - actionConnectorFields: SlackActionFields, - actionParamsFields: SlackParamsFields, - }; -} - -const SlackActionFields: React.FunctionComponent> = ({ action, editActionSecrets, errors }) => { - const { webhookUrl } = action.secrets; - - return ( - - - - - } - error={errors.webhookUrl} - isInvalid={errors.webhookUrl.length > 0 && webhookUrl !== undefined} - label={i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlTextFieldLabel', - { - defaultMessage: 'Webhook URL', - } - )} - > - 0 && webhookUrl !== undefined} - name="webhookUrl" - placeholder="Example: https://hooks.slack.com/services" - value={webhookUrl || ''} - data-test-subj="slackWebhookUrlInput" - onChange={e => { - editActionSecrets('webhookUrl', e.target.value); - }} - onBlur={() => { - if (!webhookUrl) { - editActionSecrets('webhookUrl', ''); - } - }} - /> - - - ); -}; - -const SlackParamsFields: React.FunctionComponent> = ({ - actionParams, - editAction, - index, - errors, - messageVariables, - defaultMessage, -}) => { - const { message } = actionParams; - useEffect(() => { - if (!message && defaultMessage && defaultMessage.length > 0) { - editAction('message', defaultMessage, index); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const onSelectMessageVariable = (paramsProperty: string, variable: string) => { - editAction(paramsProperty, (message ?? '').concat(` {{${variable}}}`), index); - }; - - return ( - - 0 && message !== undefined} - label={i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.messageTextAreaFieldLabel', - { - defaultMessage: 'Message', - } - )} - labelAppend={ - - onSelectMessageVariable('message', variable) - } - paramsProperty="message" - /> - } - > - 0 && message !== undefined} - name="message" - value={message || ''} - data-test-subj="slackMessageTextArea" - onChange={e => { - editAction('message', e.target.value, index); - }} - onBlur={() => { - if (!message) { - editAction('message', '', index); - } - }} - /> - - - ); -}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.test.tsx similarity index 53% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack.test.tsx rename to x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.test.tsx index e4f8599d2e46e6..1a4985f4f2a581 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.test.tsx @@ -5,10 +5,10 @@ */ import React, { FunctionComponent } from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { TypeRegistry } from '../../type_registry'; -import { registerBuiltInActionTypes } from './index'; -import { ActionTypeModel, ActionParamsProps } from '../../../types'; -import { SlackActionParams, SlackActionConnector } from './types'; +import { TypeRegistry } from '../../../type_registry'; +import { registerBuiltInActionTypes } from '.././index'; +import { ActionTypeModel, ActionParamsProps } from '../../../../types'; +import { SlackActionParams, SlackActionConnector } from '../types'; const ACTION_TYPE_ID = '.slack'; let actionTypeModel: ActionTypeModel; @@ -75,70 +75,6 @@ describe('slack action params validation', () => { errors: { message: [] }, }); }); -}); - -describe('SlackActionFields renders', () => { - test('all connector fields is rendered', () => { - expect(actionTypeModel.actionConnectorFields).not.toBeNull(); - if (!actionTypeModel.actionConnectorFields) { - return; - } - const ConnectorFields = actionTypeModel.actionConnectorFields; - const actionConnector = { - secrets: { - webhookUrl: 'http:\\test', - }, - id: 'test', - actionTypeId: '.email', - name: 'email', - config: {}, - } as SlackActionConnector; - const wrapper = mountWithIntl( - {}} - editActionSecrets={() => {}} - /> - ); - expect(wrapper.find('[data-test-subj="slackWebhookUrlInput"]').length > 0).toBeTruthy(); - expect( - wrapper - .find('[data-test-subj="slackWebhookUrlInput"]') - .first() - .prop('value') - ).toBe('http:\\test'); - }); -}); - -describe('SlackParamsFields renders', () => { - test('all params fields is rendered', () => { - expect(actionTypeModel.actionParamsFields).not.toBeNull(); - if (!actionTypeModel.actionParamsFields) { - return; - } - const ParamsFields = actionTypeModel.actionParamsFields as FunctionComponent< - ActionParamsProps - >; - const actionParams = { - message: 'test message', - }; - const wrapper = mountWithIntl( - {}} - index={0} - /> - ); - expect(wrapper.find('[data-test-subj="slackMessageTextArea"]').length > 0).toBeTruthy(); - expect( - wrapper - .find('[data-test-subj="slackMessageTextArea"]') - .first() - .prop('value') - ).toStrictEqual('test message'); - }); test('params validation fails when message is not valid', () => { const actionParams = { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.tsx new file mode 100644 index 00000000000000..bc930802f0e99e --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.tsx @@ -0,0 +1,78 @@ +/* + * 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, { lazy, Suspense } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ActionTypeModel, ValidationResult } from '../../../../types'; +import { SlackActionParams, SlackActionConnector } from '../types'; + +const SlackActionFields = lazy(() => import('./slack_connectors')); +const SlackParamsFields = lazy(() => import('./slack_params')); + +export function getActionType(): ActionTypeModel { + return { + id: '.slack', + iconClass: 'logoSlack', + selectMessage: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.selectMessageText', + { + defaultMessage: 'Send a message to a Slack channel or user.', + } + ), + actionTypeTitle: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.actionTypeTitle', + { + defaultMessage: 'Send to Slack', + } + ), + validateConnector: (action: SlackActionConnector): ValidationResult => { + const validationResult = { errors: {} }; + const errors = { + webhookUrl: new Array(), + }; + validationResult.errors = errors; + if (!action.secrets.webhookUrl) { + errors.webhookUrl.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.requiredWebhookUrlText', + { + defaultMessage: 'Webhook URL is required.', + } + ) + ); + } + return validationResult; + }, + validateParams: (actionParams: SlackActionParams): ValidationResult => { + const validationResult = { errors: {} }; + const errors = { + message: new Array(), + }; + validationResult.errors = errors; + if (!actionParams.message?.length) { + errors.message.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSlackMessageText', + { + defaultMessage: 'Message is required.', + } + ) + ); + } + return validationResult; + }, + actionConnectorFields: (props: any) => ( + }> + + + ), + actionParamsFields: (props: any) => ( + }> + + + ), + }; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.test.tsx new file mode 100644 index 00000000000000..8773e68a5da7ac --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.test.tsx @@ -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 React from 'react'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { SlackActionConnector } from '../types'; +import SlackActionFields from './slack_connectors'; + +describe('SlackActionFields renders', () => { + test('all connector fields is rendered', () => { + const actionConnector = { + secrets: { + webhookUrl: 'http:\\test', + }, + id: 'test', + actionTypeId: '.email', + name: 'email', + config: {}, + } as SlackActionConnector; + const wrapper = mountWithIntl( + {}} + editActionSecrets={() => {}} + /> + ); + expect(wrapper.find('[data-test-subj="slackWebhookUrlInput"]').length > 0).toBeTruthy(); + expect( + wrapper + .find('[data-test-subj="slackWebhookUrlInput"]') + .first() + .prop('value') + ).toBe('http:\\test'); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.tsx new file mode 100644 index 00000000000000..e8be1f1678c48e --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.tsx @@ -0,0 +1,65 @@ +/* + * 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 { EuiFieldText, EuiFormRow, EuiLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { ActionConnectorFieldsProps } from '../../../../types'; +import { SlackActionConnector } from '../types'; + +const SlackActionFields: React.FunctionComponent> = ({ action, editActionSecrets, errors }) => { + const { webhookUrl } = action.secrets; + + return ( + + + + + } + error={errors.webhookUrl} + isInvalid={errors.webhookUrl.length > 0 && webhookUrl !== undefined} + label={i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlTextFieldLabel', + { + defaultMessage: 'Webhook URL', + } + )} + > + 0 && webhookUrl !== undefined} + name="webhookUrl" + placeholder="Example: https://hooks.slack.com/services" + value={webhookUrl || ''} + data-test-subj="slackWebhookUrlInput" + onChange={e => { + editActionSecrets('webhookUrl', e.target.value); + }} + onBlur={() => { + if (!webhookUrl) { + editActionSecrets('webhookUrl', ''); + } + }} + /> + + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { SlackActionFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.test.tsx new file mode 100644 index 00000000000000..4183aeb48dec76 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.test.tsx @@ -0,0 +1,31 @@ +/* + * 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 from 'react'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import SlackParamsFields from './slack_params'; + +describe('SlackParamsFields renders', () => { + test('all params fields is rendered', () => { + const actionParams = { + message: 'test message', + }; + const wrapper = mountWithIntl( + {}} + index={0} + /> + ); + expect(wrapper.find('[data-test-subj="slackMessageTextArea"]').length > 0).toBeTruthy(); + expect( + wrapper + .find('[data-test-subj="slackMessageTextArea"]') + .first() + .prop('value') + ).toStrictEqual('test message'); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.tsx new file mode 100644 index 00000000000000..38b7fd35d06ef1 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.tsx @@ -0,0 +1,77 @@ +/* + * 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, useEffect } from 'react'; +import { EuiTextArea, EuiFormRow, EuiLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ActionParamsProps } from '../../../../types'; +import { SlackActionParams } from '../types'; +import { AddMessageVariables } from '../../add_message_variables'; + +const SlackParamsFields: React.FunctionComponent> = ({ + actionParams, + editAction, + index, + errors, + messageVariables, + defaultMessage, +}) => { + const { message } = actionParams; + useEffect(() => { + if (!message && defaultMessage && defaultMessage.length > 0) { + editAction('message', defaultMessage, index); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const onSelectMessageVariable = (paramsProperty: string, variable: string) => { + editAction(paramsProperty, (message ?? '').concat(` {{${variable}}}`), index); + }; + + return ( + + 0 && message !== undefined} + label={i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.messageTextAreaFieldLabel', + { + defaultMessage: 'Message', + } + )} + labelAppend={ + + onSelectMessageVariable('message', variable) + } + paramsProperty="message" + /> + } + > + 0 && message !== undefined} + name="message" + value={message || ''} + data-test-subj="slackMessageTextArea" + onChange={e => { + editAction('message', e.target.value, index); + }} + onBlur={() => { + if (!message) { + editAction('message', '', index); + } + }} + /> + + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { SlackParamsFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.test.tsx similarity index 50% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook.test.tsx rename to x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.test.tsx index 7d0082708075ff..d18dd73f89ebed 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.test.tsx @@ -5,10 +5,10 @@ */ import React, { FunctionComponent } from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { TypeRegistry } from '../../type_registry'; -import { registerBuiltInActionTypes } from './index'; -import { ActionTypeModel, ActionParamsProps } from '../../../types'; -import { WebhookActionParams, WebhookActionConnector } from './types'; +import { TypeRegistry } from '../../../type_registry'; +import { registerBuiltInActionTypes } from '.././index'; +import { ActionTypeModel, ActionParamsProps } from '../../../../types'; +import { WebhookActionParams, WebhookActionConnector } from '../types'; const ACTION_TYPE_ID = '.webhook'; let actionTypeModel: ActionTypeModel; @@ -91,79 +91,6 @@ describe('webhook action params validation', () => { errors: { body: [] }, }); }); -}); - -describe('WebhookActionConnectorFields renders', () => { - test('all connector fields is rendered', () => { - expect(actionTypeModel.actionConnectorFields).not.toBeNull(); - if (!actionTypeModel.actionConnectorFields) { - return; - } - const ConnectorFields = actionTypeModel.actionConnectorFields; - const actionConnector = { - secrets: { - user: 'user', - password: 'pass', - }, - id: 'test', - actionTypeId: '.webhook', - isPreconfigured: false, - name: 'webhook', - config: { - method: 'PUT', - url: 'http:\\test', - headers: { 'content-type': 'text' }, - }, - } as WebhookActionConnector; - const wrapper = mountWithIntl( - {}} - editActionSecrets={() => {}} - /> - ); - expect(wrapper.find('[data-test-subj="webhookViewHeadersSwitch"]').length > 0).toBeTruthy(); - wrapper - .find('[data-test-subj="webhookViewHeadersSwitch"]') - .first() - .simulate('click'); - expect(wrapper.find('[data-test-subj="webhookMethodSelect"]').length > 0).toBeTruthy(); - expect(wrapper.find('[data-test-subj="webhookUrlText"]').length > 0).toBeTruthy(); - expect(wrapper.find('[data-test-subj="webhookUserInput"]').length > 0).toBeTruthy(); - expect(wrapper.find('[data-test-subj="webhookPasswordInput"]').length > 0).toBeTruthy(); - }); -}); - -describe('WebhookParamsFields renders', () => { - test('all params fields is rendered', () => { - expect(actionTypeModel.actionParamsFields).not.toBeNull(); - if (!actionTypeModel.actionParamsFields) { - return; - } - const ParamsFields = actionTypeModel.actionParamsFields as FunctionComponent< - ActionParamsProps - >; - const actionParams = { - body: 'test message', - }; - const wrapper = mountWithIntl( - {}} - index={0} - /> - ); - expect(wrapper.find('[data-test-subj="webhookBodyEditor"]').length > 0).toBeTruthy(); - expect( - wrapper - .find('[data-test-subj="webhookBodyEditor"]') - .first() - .prop('value') - ).toStrictEqual('test message'); - expect(wrapper.find('[data-test-subj="bodyAddVariableButton"]').length > 0).toBeTruthy(); - }); test('params validation fails when body is not valid', () => { const actionParams = { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.tsx new file mode 100644 index 00000000000000..dd06653ef0dec6 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.tsx @@ -0,0 +1,111 @@ +/* + * 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, { lazy, Suspense } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ActionTypeModel, ValidationResult } from '../../../../types'; +import { WebhookActionParams, WebhookActionConnector } from '../types'; + +const WebhookActionConnectorFields = lazy(() => import('./webhook_connectors')); +const WebhookParamsFields = lazy(() => import('./webhook_params')); + +export function getActionType(): ActionTypeModel { + return { + id: '.webhook', + iconClass: 'logoWebhook', + selectMessage: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.selectMessageText', + { + defaultMessage: 'Send a request to a web service.', + } + ), + actionTypeTitle: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.actionTypeTitle', + { + defaultMessage: 'Webhook data', + } + ), + validateConnector: (action: WebhookActionConnector): ValidationResult => { + const validationResult = { errors: {} }; + const errors = { + url: new Array(), + method: new Array(), + user: new Array(), + password: new Array(), + }; + validationResult.errors = errors; + if (!action.config.url) { + errors.url.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.error.requiredUrlText', + { + defaultMessage: 'URL is required.', + } + ) + ); + } + if (!action.config.method) { + errors.method.push( + i18n.translate( + 'xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredMethodText', + { + defaultMessage: 'Method is required.', + } + ) + ); + } + if (!action.secrets.user && action.secrets.password) { + errors.user.push( + i18n.translate( + 'xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredHostText', + { + defaultMessage: 'Username is required.', + } + ) + ); + } + if (!action.secrets.password && action.secrets.user) { + errors.password.push( + i18n.translate( + 'xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredPasswordText', + { + defaultMessage: 'Password is required.', + } + ) + ); + } + return validationResult; + }, + validateParams: (actionParams: WebhookActionParams): ValidationResult => { + const validationResult = { errors: {} }; + const errors = { + body: new Array(), + }; + validationResult.errors = errors; + if (!actionParams.body?.length) { + errors.body.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredWebhookBodyText', + { + defaultMessage: 'Body is required.', + } + ) + ); + } + return validationResult; + }, + actionConnectorFields: (props: any) => ( + }> + + + ), + actionParamsFields: (props: any) => ( + }> + + + ), + }; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx new file mode 100644 index 00000000000000..6f6c9d7f095317 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx @@ -0,0 +1,46 @@ +/* + * 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 from 'react'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { WebhookActionConnector } from '../types'; +import WebhookActionConnectorFields from './webhook_connectors'; + +describe('WebhookActionConnectorFields renders', () => { + test('all connector fields is rendered', () => { + const actionConnector = { + secrets: { + user: 'user', + password: 'pass', + }, + id: 'test', + actionTypeId: '.webhook', + isPreconfigured: false, + name: 'webhook', + config: { + method: 'PUT', + url: 'http:\\test', + headers: { 'content-type': 'text' }, + }, + } as WebhookActionConnector; + const wrapper = mountWithIntl( + {}} + editActionSecrets={() => {}} + /> + ); + expect(wrapper.find('[data-test-subj="webhookViewHeadersSwitch"]').length > 0).toBeTruthy(); + wrapper + .find('[data-test-subj="webhookViewHeadersSwitch"]') + .first() + .simulate('click'); + expect(wrapper.find('[data-test-subj="webhookMethodSelect"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="webhookUrlText"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="webhookUserInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="webhookPasswordInput"]').length > 0).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx similarity index 71% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook.tsx rename to x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx index daa5a6caeabe9c..e163463602d9f2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx @@ -19,112 +19,15 @@ import { EuiDescriptionListDescription, EuiDescriptionListTitle, EuiTitle, - EuiCodeEditor, EuiSwitch, EuiButtonEmpty, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { - ActionTypeModel, - ActionConnectorFieldsProps, - ValidationResult, - ActionParamsProps, -} from '../../../types'; -import { WebhookActionParams, WebhookActionConnector } from './types'; -import { AddMessageVariables } from '../add_message_variables'; +import { ActionConnectorFieldsProps } from '../../../../types'; +import { WebhookActionConnector } from '../types'; const HTTP_VERBS = ['post', 'put']; -export function getActionType(): ActionTypeModel { - return { - id: '.webhook', - iconClass: 'logoWebhook', - selectMessage: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.selectMessageText', - { - defaultMessage: 'Send a request to a web service.', - } - ), - actionTypeTitle: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.actionTypeTitle', - { - defaultMessage: 'Webhook data', - } - ), - validateConnector: (action: WebhookActionConnector): ValidationResult => { - const validationResult = { errors: {} }; - const errors = { - url: new Array(), - method: new Array(), - user: new Array(), - password: new Array(), - }; - validationResult.errors = errors; - if (!action.config.url) { - errors.url.push( - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.error.requiredUrlText', - { - defaultMessage: 'URL is required.', - } - ) - ); - } - if (!action.config.method) { - errors.method.push( - i18n.translate( - 'xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredMethodText', - { - defaultMessage: 'Method is required.', - } - ) - ); - } - if (!action.secrets.user && action.secrets.password) { - errors.user.push( - i18n.translate( - 'xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredHostText', - { - defaultMessage: 'Username is required.', - } - ) - ); - } - if (!action.secrets.password && action.secrets.user) { - errors.password.push( - i18n.translate( - 'xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredPasswordText', - { - defaultMessage: 'Password is required.', - } - ) - ); - } - return validationResult; - }, - validateParams: (actionParams: WebhookActionParams): ValidationResult => { - const validationResult = { errors: {} }; - const errors = { - body: new Array(), - }; - validationResult.errors = errors; - if (!actionParams.body?.length) { - errors.body.push( - i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredWebhookBodyText', - { - defaultMessage: 'Body is required.', - } - ) - ); - } - return validationResult; - }, - actionConnectorFields: WebhookActionConnectorFields, - actionParamsFields: WebhookParamsFields, - }; -} - const WebhookActionConnectorFields: React.FunctionComponent> = ({ action, editActionConfig, editActionSecrets, errors }) => { @@ -457,56 +360,5 @@ const WebhookActionConnectorFields: React.FunctionComponent> = ({ - actionParams, - editAction, - index, - messageVariables, - errors, -}) => { - const { body } = actionParams; - const onSelectMessageVariable = (paramsProperty: string, variable: string) => { - editAction(paramsProperty, (body ?? '').concat(` {{${variable}}}`), index); - }; - return ( - - 0 && body !== undefined} - fullWidth - error={errors.body} - labelAppend={ - onSelectMessageVariable('body', variable)} - paramsProperty="body" - /> - } - > - { - editAction('body', json, index); - }} - /> - - - ); -}; +// eslint-disable-next-line import/no-default-export +export { WebhookActionConnectorFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.test.tsx new file mode 100644 index 00000000000000..5ca27a53083f95 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.test.tsx @@ -0,0 +1,32 @@ +/* + * 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 from 'react'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import WebhookParamsFields from './webhook_params'; + +describe('WebhookParamsFields renders', () => { + test('all params fields is rendered', () => { + const actionParams = { + body: 'test message', + }; + const wrapper = mountWithIntl( + {}} + index={0} + /> + ); + expect(wrapper.find('[data-test-subj="webhookBodyEditor"]').length > 0).toBeTruthy(); + expect( + wrapper + .find('[data-test-subj="webhookBodyEditor"]') + .first() + .prop('value') + ).toStrictEqual('test message'); + expect(wrapper.find('[data-test-subj="bodyAddVariableButton"]').length > 0).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.tsx new file mode 100644 index 00000000000000..9e802b96e16be3 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_params.tsx @@ -0,0 +1,68 @@ +/* + * 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 { EuiFormRow, EuiCodeEditor } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ActionParamsProps } from '../../../../types'; +import { WebhookActionParams } from '../types'; +import { AddMessageVariables } from '../../add_message_variables'; + +const WebhookParamsFields: React.FunctionComponent> = ({ + actionParams, + editAction, + index, + messageVariables, + errors, +}) => { + const { body } = actionParams; + const onSelectMessageVariable = (paramsProperty: string, variable: string) => { + editAction(paramsProperty, (body ?? '').concat(` {{${variable}}}`), index); + }; + return ( + + 0 && body !== undefined} + fullWidth + error={errors.body} + labelAppend={ + onSelectMessageVariable('body', variable)} + paramsProperty="body" + /> + } + > + { + editAction('body', json, index); + }} + /> + + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { WebhookParamsFields as default }; From ab4b5eb513c64fc89ea3cdefa6aec39ca2124fe3 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Tue, 5 May 2020 13:47:14 +0100 Subject: [PATCH 05/22] use typing to ensure lazy loading --- .../siem/public/lib/connectors/servicenow.tsx | 187 +------- .../lib/connectors/servicenow_connectors.tsx | 188 ++++++++ .../lib/connectors/servicenow_params.tsx | 21 + .../builtin_action_types/email/email.tsx | 20 +- .../email/email_connector.tsx | 9 +- .../es_index/es_index.tsx | 22 +- .../pagerduty/pagerduty.test.tsx | 11 +- .../pagerduty/pagerduty.tsx | 20 +- .../pagerduty/pagerduty_connectors.tsx | 406 +----------------- .../pagerduty/pagerduty_params.test.tsx | 12 +- .../pagerduty/pagerduty_params.tsx | 22 +- .../server_log/server_log.test.tsx | 5 +- .../server_log/server_log.tsx | 13 +- .../builtin_action_types/slack/slack.test.tsx | 6 +- .../builtin_action_types/slack/slack.tsx | 20 +- .../slack/slack_params.tsx | 2 +- .../webhook/webhook.test.tsx | 6 +- .../builtin_action_types/webhook/webhook.tsx | 20 +- .../action_connector_form.tsx | 19 +- .../action_connector_form/action_form.tsx | 21 +- .../public/application/type_registry.test.ts | 6 +- .../triggers_actions_ui/public/types.ts | 19 +- 22 files changed, 298 insertions(+), 757 deletions(-) create mode 100644 x-pack/plugins/siem/public/lib/connectors/servicenow_connectors.tsx create mode 100644 x-pack/plugins/siem/public/lib/connectors/servicenow_params.tsx diff --git a/x-pack/plugins/siem/public/lib/connectors/servicenow.tsx b/x-pack/plugins/siem/public/lib/connectors/servicenow.tsx index 9fe0b4a957cebd..3610a0423002c7 100644 --- a/x-pack/plugins/siem/public/lib/connectors/servicenow.tsx +++ b/x-pack/plugins/siem/public/lib/connectors/servicenow.tsx @@ -3,39 +3,23 @@ * 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, { useCallback, ChangeEvent, useEffect } from 'react'; +import { lazy } from 'react'; import { - EuiFieldText, - EuiFlexGroup, - EuiFlexItem, - EuiFormRow, - EuiFieldPassword, - EuiSpacer, -} from '@elastic/eui'; - -import { isEmpty, get } from 'lodash/fp'; - -import { - ActionConnectorFieldsProps, ActionTypeModel, ValidationResult, - ActionParamsProps, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../triggers_actions_ui/public/types'; -import { FieldMapping } from '../../pages/case/components/configure_cases/field_mapping'; - import * as i18n from './translations'; import { ServiceNowActionConnector } from './types'; import { isUrlInvalid } from './validators'; -import { connectors, defaultMapping } from './config'; -import { CasesConfigurationMapping } from '../../containers/case/configure/types'; +import { connectors } from './config'; const serviceNowDefinition = connectors['.servicenow']; -interface ServiceNowActionParams { +export interface ServiceNowActionParams { message: string; } @@ -79,168 +63,7 @@ export function getActionType(): ActionTypeModel { validateParams: (actionParams: ServiceNowActionParams): ValidationResult => { return { errors: {} }; }, - actionConnectorFields: ServiceNowConnectorFields, - actionParamsFields: ServiceNowParamsFields, + actionConnectorFields: lazy(() => import('./servicenow_connectors')), + actionParamsFields: lazy(() => import('./servicenow_params')), }; } - -const ServiceNowConnectorFields: React.FunctionComponent> = ({ action, editActionConfig, editActionSecrets, errors }) => { - /* We do not provide defaults values to the fields (like empty string for apiUrl) intentionally. - * If we do, errors will be shown the first time the flyout is open even though the user did not - * interact with the form. Also, we would like to show errors for empty fields provided by the user. - /*/ - const { apiUrl, casesConfiguration: { mapping = [] } = {} } = action.config; - const { username, password } = action.secrets; - - const isApiUrlInvalid: boolean = errors.apiUrl.length > 0 && apiUrl != null; - const isUsernameInvalid: boolean = errors.username.length > 0 && username != null; - const isPasswordInvalid: boolean = errors.password.length > 0 && password != null; - - /** - * We need to distinguish between the add flyout and the edit flyout. - * useEffect will run only once on component mount. - * This guarantees that the function below will run only once. - * On the first render of the component the apiUrl can be either undefined or filled. - * If it is filled then we are on the edit flyout. Otherwise we are on the add flyout. - */ - - useEffect(() => { - if (!isEmpty(apiUrl)) { - editActionSecrets('username', ''); - editActionSecrets('password', ''); - } - }, []); - - if (isEmpty(mapping)) { - editActionConfig('casesConfiguration', { - ...action.config.casesConfiguration, - mapping: defaultMapping, - }); - } - - const handleOnChangeActionConfig = useCallback( - (key: string, evt: ChangeEvent) => editActionConfig(key, evt.target.value), - [] - ); - - const handleOnBlurActionConfig = useCallback( - (key: string) => { - if (key === 'apiUrl' && action.config[key] == null) { - editActionConfig(key, ''); - } - }, - [action.config] - ); - - const handleOnChangeSecretConfig = useCallback( - (key: string, evt: ChangeEvent) => editActionSecrets(key, evt.target.value), - [] - ); - - const handleOnBlurSecretConfig = useCallback( - (key: string) => { - if (['username', 'password'].includes(key) && get(key, action.secrets) == null) { - editActionSecrets(key, ''); - } - }, - [action.secrets] - ); - - const handleOnChangeMappingConfig = useCallback( - (newMapping: CasesConfigurationMapping[]) => - editActionConfig('casesConfiguration', { - ...action.config.casesConfiguration, - mapping: newMapping, - }), - [action.config] - ); - - return ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; - -const ServiceNowParamsFields: React.FunctionComponent> = ({ actionParams, editAction, index, errors }) => { - return null; -}; diff --git a/x-pack/plugins/siem/public/lib/connectors/servicenow_connectors.tsx b/x-pack/plugins/siem/public/lib/connectors/servicenow_connectors.tsx new file mode 100644 index 00000000000000..5c82955ee0f738 --- /dev/null +++ b/x-pack/plugins/siem/public/lib/connectors/servicenow_connectors.tsx @@ -0,0 +1,188 @@ +/* + * 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, { useCallback, ChangeEvent, useEffect } from 'react'; +import { + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiFieldPassword, + EuiSpacer, +} from '@elastic/eui'; + +import { isEmpty, get } from 'lodash/fp'; + +import { + ActionConnectorFieldsProps, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../triggers_actions_ui/public/types'; + +import { FieldMapping } from '../../pages/case/components/configure_cases/field_mapping'; + +import * as i18n from './translations'; + +import { ServiceNowActionConnector } from './types'; + +import { defaultMapping } from './config'; +import { CasesConfigurationMapping } from '../../containers/case/configure/types'; + +const ServiceNowConnectorFields: React.FunctionComponent> = ({ action, editActionConfig, editActionSecrets, errors }) => { + /* We do not provide defaults values to the fields (like empty string for apiUrl) intentionally. + * If we do, errors will be shown the first time the flyout is open even though the user did not + * interact with the form. Also, we would like to show errors for empty fields provided by the user. + /*/ + const { apiUrl, casesConfiguration: { mapping = [] } = {} } = action.config; + const { username, password } = action.secrets; + + const isApiUrlInvalid: boolean = errors.apiUrl.length > 0 && apiUrl != null; + const isUsernameInvalid: boolean = errors.username.length > 0 && username != null; + const isPasswordInvalid: boolean = errors.password.length > 0 && password != null; + + /** + * We need to distinguish between the add flyout and the edit flyout. + * useEffect will run only once on component mount. + * This guarantees that the function below will run only once. + * On the first render of the component the apiUrl can be either undefined or filled. + * If it is filled then we are on the edit flyout. Otherwise we are on the add flyout. + */ + + useEffect(() => { + if (!isEmpty(apiUrl)) { + editActionSecrets('username', ''); + editActionSecrets('password', ''); + } + }, []); + + if (isEmpty(mapping)) { + editActionConfig('casesConfiguration', { + ...action.config.casesConfiguration, + mapping: defaultMapping, + }); + } + + const handleOnChangeActionConfig = useCallback( + (key: string, evt: ChangeEvent) => editActionConfig(key, evt.target.value), + [] + ); + + const handleOnBlurActionConfig = useCallback( + (key: string) => { + if (key === 'apiUrl' && action.config[key] == null) { + editActionConfig(key, ''); + } + }, + [action.config] + ); + + const handleOnChangeSecretConfig = useCallback( + (key: string, evt: ChangeEvent) => editActionSecrets(key, evt.target.value), + [] + ); + + const handleOnBlurSecretConfig = useCallback( + (key: string) => { + if (['username', 'password'].includes(key) && get(key, action.secrets) == null) { + editActionSecrets(key, ''); + } + }, + [action.secrets] + ); + + const handleOnChangeMappingConfig = useCallback( + (newMapping: CasesConfigurationMapping[]) => + editActionConfig('casesConfiguration', { + ...action.config.casesConfiguration, + mapping: newMapping, + }), + [action.config] + ); + + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { ServiceNowConnectorFields as default }; diff --git a/x-pack/plugins/siem/public/lib/connectors/servicenow_params.tsx b/x-pack/plugins/siem/public/lib/connectors/servicenow_params.tsx new file mode 100644 index 00000000000000..8eef20b2c74747 --- /dev/null +++ b/x-pack/plugins/siem/public/lib/connectors/servicenow_params.tsx @@ -0,0 +1,21 @@ +/* + * 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 from 'react'; + +import { + ActionParamsProps, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../triggers_actions_ui/public/types'; +import { ServiceNowActionParams } from './servicenow'; + +const ServiceNowParamsFields: React.FunctionComponent> = ({ actionParams, editAction, index, errors }) => { + return null; +}; + +// eslint-disable-next-line import/no-default-export +export { ServiceNowParamsFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx index d572fb5cd17214..abb102c04b0547 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx @@ -3,16 +3,12 @@ * 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, { lazy, Suspense } from 'react'; -import { EuiLoadingSpinner } from '@elastic/eui'; +import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import { ActionTypeModel, ValidationResult } from '../../../../types'; import { EmailActionParams, EmailActionConnector } from '../types'; -const EmailActionConnectorFields = lazy(() => import('./email_connector')); -const EmailParamsFields = lazy(() => import('./email_params')); - -export function getActionType(): ActionTypeModel { +export function getActionType(): ActionTypeModel { const mailformat = /^[^@\s]+@[^@\s]+$/; return { id: '.email', @@ -148,15 +144,7 @@ export function getActionType(): ActionTypeModel { } return validationResult; }, - actionConnectorFields: (props: any) => ( - }> - - - ), - actionParamsFields: (props: any) => ( - }> - - - ), + actionConnectorFields: lazy(() => import('./email_connector')), + actionParamsFields: lazy(() => import('./email_params')), }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx index e41661ca36007e..4ef4c8a4d8617f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx @@ -17,12 +17,9 @@ import { i18n } from '@kbn/i18n'; import { ActionConnectorFieldsProps } from '../../../../types'; import { EmailActionConnector } from '../types'; -export const EmailActionConnectorFields = ({ - action, - editActionConfig, - editActionSecrets, - errors, -}: ActionConnectorFieldsProps) => { +export const EmailActionConnectorFields: React.FunctionComponent> = ({ action, editActionConfig, editActionSecrets, errors }) => { const { from, host, port, secure } = action.config; const { user, password } = action.secrets; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.tsx index 3ee2c4dd4b7346..3ee663a5fc8a06 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.tsx @@ -3,16 +3,12 @@ * 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, { lazy, Suspense } from 'react'; -import { EuiLoadingSpinner } from '@elastic/eui'; +import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import { ActionTypeModel, ValidationResult } from '../../../../types'; -import { EsIndexActionConnector } from '.././types'; +import { EsIndexActionConnector, IndexActionParams } from '../types'; -const IndexActionConnectorFields = lazy(() => import('./es_index_connector')); -const IndexParamsFields = lazy(() => import('./es_index_params')); - -export function getActionType(): ActionTypeModel { +export function getActionType(): ActionTypeModel { return { id: '.index', iconClass: 'indexOpen', @@ -46,16 +42,8 @@ export function getActionType(): ActionTypeModel { } return validationResult; }, - actionConnectorFields: (props: any) => ( - }> - - - ), - actionParamsFields: (props: any) => ( - }> - - - ), + actionConnectorFields: lazy(() => import('./es_index_connector')), + actionParamsFields: lazy(() => import('./es_index_params')), validateParams: (): ValidationResult => { return { errors: {} }; }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.test.tsx index d9aa72d5932cb6..ba7eb598c120d2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.test.tsx @@ -3,17 +3,10 @@ * 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, { FunctionComponent } from 'react'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { TypeRegistry } from '../../../type_registry'; import { registerBuiltInActionTypes } from '.././index'; -import { ActionTypeModel, ActionParamsProps } from '../../../../types'; -import { - PagerDutyActionParams, - EventActionOptions, - SeverityActionOptions, - PagerDutyActionConnector, -} from '.././types'; +import { ActionTypeModel } from '../../../../types'; +import { PagerDutyActionConnector } from '.././types'; const ACTION_TYPE_ID = '.pagerduty'; let actionTypeModel: ActionTypeModel; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx index 6f214525de259c..477ed3497ad180 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx @@ -3,8 +3,7 @@ * 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, { lazy, Suspense } from 'react'; -import { EuiLoadingSpinner } from '@elastic/eui'; +import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import moment from 'moment'; import { ActionTypeModel, ValidationResult } from '../../../../types'; @@ -12,10 +11,7 @@ import { PagerDutyActionParams, PagerDutyActionConnector } from '.././types'; import pagerDutySvg from './pagerduty.svg'; import { hasMustacheTokens } from '../../../lib/has_mustache_tokens'; -const PagerDutyActionConnectorFields = lazy(() => import('./pagerduty_connectors')); -const PagerDutyParamsFields = lazy(() => import('./pagerduty_params')); - -export function getActionType(): ActionTypeModel { +export function getActionType(): ActionTypeModel { return { id: '.pagerduty', iconClass: pagerDutySvg, @@ -86,16 +82,8 @@ export function getActionType(): ActionTypeModel { } return validationResult; }, - actionConnectorFields: (props: any) => ( - }> - - - ), - actionParamsFields: (props: any) => ( - }> - - - ), + actionConnectorFields: lazy(() => import('./pagerduty_connectors')), + actionParamsFields: lazy(() => import('./pagerduty_params')), }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx index 83bf8c5488f3d7..99be15d7aed66b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx @@ -4,27 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ import React, { Fragment } from 'react'; -import { - EuiFieldText, - EuiFlexGroup, - EuiFlexItem, - EuiFormRow, - EuiSelect, - EuiLink, -} from '@elastic/eui'; +import { EuiFieldText, EuiFormRow, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import moment from 'moment'; -import { - ActionTypeModel, - ActionConnectorFieldsProps, - ValidationResult, - ActionParamsProps, -} from '../../../../types'; -import { PagerDutyActionParams, PagerDutyActionConnector } from '.././types'; -import pagerDutySvg from '.././pagerduty.svg'; -import { AddMessageVariables } from '../../add_message_variables'; -import { hasMustacheTokens } from '../../../lib/has_mustache_tokens'; +import { ActionConnectorFieldsProps } from '../../../../types'; +import { PagerDutyActionConnector } from '.././types'; const PagerDutyActionConnectorFields: React.FunctionComponent> = ({ - actionParams, - editAction, - index, - messageVariables, - errors, -}) => { - const { - eventAction, - dedupKey, - summary, - source, - severity, - timestamp, - component, - group, - } = actionParams; - const severityOptions = [ - { - value: 'info', - text: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectInfoOptionLabel', - { - defaultMessage: 'Info', - } - ), - }, - { - value: 'critical', - text: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectCriticalOptionLabel', - { - defaultMessage: 'Critical', - } - ), - }, - { - value: 'warning', - text: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectWarningOptionLabel', - { - defaultMessage: 'Warning', - } - ), - }, - { - value: 'error', - text: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.severitySelectErrorOptionLabel', - { - defaultMessage: 'Error', - } - ), - }, - ]; - const eventActionOptions = [ - { - value: 'trigger', - text: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectTriggerOptionLabel', - { - defaultMessage: 'Trigger', - } - ), - }, - { - value: 'resolve', - text: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectResolveOptionLabel', - { - defaultMessage: 'Resolve', - } - ), - }, - { - value: 'acknowledge', - text: i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectAcknowledgeOptionLabel', - { - defaultMessage: 'Acknowledge', - } - ), - }, - ]; - - const onSelectMessageVariable = (paramsProperty: string, variable: string) => { - editAction( - paramsProperty, - ((actionParams as any)[paramsProperty] ?? '').concat(` {{${variable}}}`), - index - ); - }; - - return ( - - - - - { - editAction('severity', e.target.value, index); - }} - /> - - - - - { - editAction('eventAction', e.target.value, index); - }} - /> - - - - - - - onSelectMessageVariable('dedupKey', variable) - } - paramsProperty="dedupKey" - /> - } - > - ) => { - editAction('dedupKey', e.target.value, index); - }} - onBlur={() => { - if (!dedupKey) { - editAction('dedupKey', '', index); - } - }} - /> - - - - 0 && timestamp !== undefined} - label={i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.timestampTextFieldLabel', - { - defaultMessage: 'Timestamp (optional)', - } - )} - labelAppend={ - - onSelectMessageVariable('timestamp', variable) - } - paramsProperty="timestamp" - /> - } - > - 0 && timestamp !== undefined} - onChange={(e: React.ChangeEvent) => { - editAction('timestamp', e.target.value, index); - }} - onBlur={() => { - if (timestamp?.trim()) { - editAction('timestamp', timestamp.trim(), index); - } else { - editAction('timestamp', '', index); - } - }} - /> - - - - - onSelectMessageVariable('component', variable) - } - paramsProperty="component" - /> - } - > - ) => { - editAction('component', e.target.value, index); - }} - onBlur={() => { - if (!component) { - editAction('component', '', index); - } - }} - /> - - onSelectMessageVariable('group', variable)} - paramsProperty="group" - /> - } - > - ) => { - editAction('group', e.target.value, index); - }} - onBlur={() => { - if (!group) { - editAction('group', '', index); - } - }} - /> - - onSelectMessageVariable('source', variable)} - paramsProperty="source" - /> - } - > - ) => { - editAction('source', e.target.value, index); - }} - onBlur={() => { - if (!source) { - editAction('source', '', index); - } - }} - /> - - 0 && summary !== undefined} - label={i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.summaryFieldLabel', - { - defaultMessage: 'Summary', - } - )} - labelAppend={ - - onSelectMessageVariable('summary', variable) - } - paramsProperty="summary" - /> - } - > - 0 && summary !== undefined} - name="summary" - value={summary || ''} - data-test-subj="pagerdutySummaryInput" - onChange={(e: React.ChangeEvent) => { - editAction('summary', e.target.value, index); - }} - onBlur={() => { - if (!summary) { - editAction('summary', '', index); - } - }} - /> - - onSelectMessageVariable('class', variable)} - paramsProperty="class" - /> - } - > - ) => { - editAction('class', e.target.value, index); - }} - onBlur={() => { - if (!actionParams.class) { - editAction('class', '', index); - } - }} - /> - - - ); -}; - // eslint-disable-next-line import/no-default-export export { PagerDutyActionConnectorFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.test.tsx index 63584e6d800b91..d1b32f545c335d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.test.tsx @@ -3,17 +3,9 @@ * 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, { FunctionComponent } from 'react'; +import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { TypeRegistry } from '../../../type_registry'; -import { registerBuiltInActionTypes } from '.././index'; -import { ActionTypeModel, ActionParamsProps } from '../../../../types'; -import { - PagerDutyActionParams, - EventActionOptions, - SeverityActionOptions, - PagerDutyActionConnector, -} from '.././types'; +import { EventActionOptions, SeverityActionOptions } from '.././types'; import PagerDutyParamsFields from './pagerduty_params'; describe('PagerDutyParamsFields renders', () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx index a017c1e26b07da..381d2e65761da3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx @@ -4,27 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ import React, { Fragment } from 'react'; -import { - EuiFieldText, - EuiFlexGroup, - EuiFlexItem, - EuiFormRow, - EuiSelect, - EuiLink, -} from '@elastic/eui'; +import { EuiFieldText, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSelect } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import moment from 'moment'; -import { - ActionTypeModel, - ActionConnectorFieldsProps, - ValidationResult, - ActionParamsProps, -} from '../../../../types'; -import { PagerDutyActionParams, PagerDutyActionConnector } from '.././types'; -import pagerDutySvg from '.././pagerduty.svg'; +import { ActionParamsProps } from '../../../../types'; +import { PagerDutyActionParams } from '.././types'; import { AddMessageVariables } from '../../add_message_variables'; -import { hasMustacheTokens } from '../../../lib/has_mustache_tokens'; const PagerDutyParamsFields: React.FunctionComponent> = ({ actionParams, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx index 39fb4cdc10904f..3bb5ea68a3040a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx @@ -3,12 +3,9 @@ * 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, { FunctionComponent } from 'react'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { TypeRegistry } from '../../../type_registry'; import { registerBuiltInActionTypes } from '.././index'; -import { ActionTypeModel, ActionConnector, ActionParamsProps } from '../../../../types'; -import { ServerLogActionParams, ServerLogLevelOptions } from '../types'; +import { ActionTypeModel, ActionConnector } from '../../../../types'; const ACTION_TYPE_ID = '.server-log'; let actionTypeModel: ActionTypeModel; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.tsx index 9de670fefbccd1..390ccf6a494e90 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.tsx @@ -3,15 +3,12 @@ * 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, { lazy, Suspense } from 'react'; +import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiLoadingSpinner } from '@elastic/eui'; import { ActionTypeModel, ValidationResult } from '../../../../types'; import { ServerLogActionParams } from '../types'; -const ServerLogParamsFields = lazy(() => import('./server_log_params')); - -export function getActionType(): ActionTypeModel { +export function getActionType(): ActionTypeModel { return { id: '.server-log', iconClass: 'logsApp', @@ -49,10 +46,6 @@ export function getActionType(): ActionTypeModel { return validationResult; }, actionConnectorFields: null, - actionParamsFields: (props: any) => ( - }> - - - ), + actionParamsFields: lazy(() => import('./server_log_params')), }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.test.tsx index 1a4985f4f2a581..7944e2b01056ed 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.test.tsx @@ -3,12 +3,10 @@ * 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, { FunctionComponent } from 'react'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { TypeRegistry } from '../../../type_registry'; import { registerBuiltInActionTypes } from '.././index'; -import { ActionTypeModel, ActionParamsProps } from '../../../../types'; -import { SlackActionParams, SlackActionConnector } from '../types'; +import { ActionTypeModel } from '../../../../types'; +import { SlackActionConnector } from '../types'; const ACTION_TYPE_ID = '.slack'; let actionTypeModel: ActionTypeModel; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.tsx index bc930802f0e99e..5d39cdb5ac387b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.tsx @@ -3,16 +3,12 @@ * 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, { lazy, Suspense } from 'react'; -import { EuiLoadingSpinner } from '@elastic/eui'; +import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import { ActionTypeModel, ValidationResult } from '../../../../types'; import { SlackActionParams, SlackActionConnector } from '../types'; -const SlackActionFields = lazy(() => import('./slack_connectors')); -const SlackParamsFields = lazy(() => import('./slack_params')); - -export function getActionType(): ActionTypeModel { +export function getActionType(): ActionTypeModel { return { id: '.slack', iconClass: 'logoSlack', @@ -64,15 +60,7 @@ export function getActionType(): ActionTypeModel { } return validationResult; }, - actionConnectorFields: (props: any) => ( - }> - - - ), - actionParamsFields: (props: any) => ( - }> - - - ), + actionConnectorFields: lazy(() => import('./slack_connectors')), + actionParamsFields: lazy(() => import('./slack_params')), }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.tsx index 38b7fd35d06ef1..42fefdd41ef673 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import React, { Fragment, useEffect } from 'react'; -import { EuiTextArea, EuiFormRow, EuiLink } from '@elastic/eui'; +import { EuiTextArea, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ActionParamsProps } from '../../../../types'; import { SlackActionParams } from '../types'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.test.tsx index d18dd73f89ebed..3413465d70d935 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.test.tsx @@ -3,12 +3,10 @@ * 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, { FunctionComponent } from 'react'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { TypeRegistry } from '../../../type_registry'; import { registerBuiltInActionTypes } from '.././index'; -import { ActionTypeModel, ActionParamsProps } from '../../../../types'; -import { WebhookActionParams, WebhookActionConnector } from '../types'; +import { ActionTypeModel } from '../../../../types'; +import { WebhookActionConnector } from '../types'; const ACTION_TYPE_ID = '.webhook'; let actionTypeModel: ActionTypeModel; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.tsx index dd06653ef0dec6..9f33e4491233a7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.tsx @@ -3,16 +3,12 @@ * 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, { lazy, Suspense } from 'react'; -import { EuiLoadingSpinner } from '@elastic/eui'; +import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import { ActionTypeModel, ValidationResult } from '../../../../types'; import { WebhookActionParams, WebhookActionConnector } from '../types'; -const WebhookActionConnectorFields = lazy(() => import('./webhook_connectors')); -const WebhookParamsFields = lazy(() => import('./webhook_params')); - -export function getActionType(): ActionTypeModel { +export function getActionType(): ActionTypeModel { return { id: '.webhook', iconClass: 'logoWebhook', @@ -97,15 +93,7 @@ export function getActionType(): ActionTypeModel { } return validationResult; }, - actionConnectorFields: (props: any) => ( - }> - - - ), - actionParamsFields: (props: any) => ( - }> - - - ), + actionConnectorFields: lazy(() => import('./webhook_connectors')), + actionParamsFields: lazy(() => import('./webhook_params')), }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx index 57333d80327939..ebf90345cfdb59 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx @@ -3,7 +3,7 @@ * 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 React, { Fragment, Suspense } from 'react'; import { EuiForm, EuiCallOut, @@ -12,6 +12,7 @@ import { EuiSpacer, EuiFieldText, EuiFormRow, + EuiLoadingSpinner, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -146,13 +147,15 @@ export const ActionConnectorForm = ({ {FieldsComponent !== null ? ( - + }> + + ) : null} ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx index 0027837c913d16..aacdfa5825adb6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, useState, useEffect } from 'react'; +import React, { Fragment, Suspense, useState, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { @@ -27,6 +27,7 @@ import { EuiCallOut, EuiHorizontalRule, EuiText, + EuiLoadingSpinner, } from '@elastic/eui'; import { HttpSetup, ToastsApi } from 'kibana/public'; import { loadActionTypes, loadAllActions as loadConnectors } from '../../lib/action_connector_api'; @@ -260,14 +261,16 @@ export const ActionForm = ({ {ParamsFieldsComponent ? ( - + }> + + ) : null} ) : ( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/type_registry.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/type_registry.test.ts index 93e61cf5b4f43e..62173a6196b98e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/type_registry.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/type_registry.test.ts @@ -23,7 +23,11 @@ const getTestAlertType = (id?: string, name?: string, iconClass?: string) => { }; }; -const getTestActionType = (id?: string, iconClass?: string, selectedMessage?: string) => { +const getTestActionType = ( + id?: string, + iconClass?: string, + selectedMessage?: string +): ActionTypeModel => { return { id: id || 'my-action-type', iconClass: iconClass || 'test', diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 7f78d327d01225..24fe39905af2b1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { HttpSetup } from 'kibana/public'; +import { ComponentType } from 'react'; import { ActionGroup } from '../../alerting/common'; import { ActionType } from '../../actions/common'; import { TypeRegistry } from './application/type_registry'; @@ -19,14 +20,16 @@ export { ActionType }; export type ActionTypeIndex = Record; export type AlertTypeIndex = Record; -export type ActionTypeRegistryContract = PublicMethodsOf>; +export type ActionTypeRegistryContract = PublicMethodsOf< + TypeRegistry> +>; export type AlertTypeRegistryContract = PublicMethodsOf>; export interface ActionConnectorFieldsProps { action: TActionConnector; editActionConfig: (property: string, value: any) => void; editActionSecrets: (property: string, value: any) => void; - errors: { [key: string]: string[] }; + errors: IErrorObject; http?: HttpSetup; } @@ -34,7 +37,7 @@ export interface ActionParamsProps { actionParams: TParams; index: number; editAction: (property: string, value: any, index: number) => void; - errors: { [key: string]: string[] }; + errors: IErrorObject; messageVariables?: string[]; defaultMessage?: string; } @@ -44,15 +47,19 @@ export interface Pagination { size: number; } -export interface ActionTypeModel { +export interface ActionTypeModel { id: string; iconClass: string; selectMessage: string; actionTypeTitle?: string; validateConnector: (connector: any) => ValidationResult; validateParams: (actionParams: any) => ValidationResult; - actionConnectorFields: React.FunctionComponent | null; - actionParamsFields: any; + actionConnectorFields: React.LazyExoticComponent< + ComponentType> + > | null; + actionParamsFields: React.LazyExoticComponent< + ComponentType> + > | null; } export interface ValidationResult { From 20e11cb1321d234b689b3a0211748371010579ed Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Tue, 5 May 2020 14:22:54 +0100 Subject: [PATCH 06/22] lazy load sections --- .../public/application/app.tsx | 31 +++++++++++++++---- .../public/application/home.tsx | 3 ++ .../components/alert_details_route.tsx | 6 ++-- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx index 0593940a0d105d..327b148fa3e466 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx @@ -3,8 +3,8 @@ * 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 from 'react'; -import { Switch, Route, Redirect, HashRouter } from 'react-router-dom'; +import React, { lazy, Suspense } from 'react'; +import { Switch, Route, Redirect, HashRouter, RouteComponentProps } from 'react-router-dom'; import { ChromeStart, DocLinksStart, @@ -15,17 +15,21 @@ import { ChromeBreadcrumb, CoreStart, } from 'kibana/public'; +import { EuiLoadingSpinner } from '@elastic/eui'; import { BASE_PATH, Section, routeToAlertDetails } from './constants'; -import { TriggersActionsUIHome } from './home'; import { AppContextProvider, useAppDependencies } from './app_context'; import { hasShowAlertsCapability } from './lib/capabilities'; import { ActionTypeModel, AlertTypeModel } from '../types'; import { TypeRegistry } from './type_registry'; -import { AlertDetailsRouteWithApi as AlertDetailsRoute } from './sections/alert_details/components/alert_details_route'; import { ChartsPluginStart } from '../../../../../src/plugins/charts/public'; import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; import { PluginStartContract as AlertingStart } from '../../../alerting/public'; +const TriggersActionsUIHome = lazy(() => import('./home')); +const AlertDetailsRoute = lazy(() => + import('./sections/alert_details/components/alert_details_route') +); + export interface AppDeps { dataPlugin: DataPublicPluginStart; charts: ChartsPluginStart; @@ -62,9 +66,24 @@ export const AppWithoutRouter = ({ sectionsRegex }: { sectionsRegex: string }) = const DEFAULT_SECTION: Section = canShowAlerts ? 'alerts' : 'connectors'; return ( - - {canShowAlerts && } + + {canShowAlerts && ( + + )} ); }; + +function suspendedRouteComponent( + RouteComponent: React.ComponentType> +) { + return (props: RouteComponentProps) => ( + }> + + + ); +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/home.tsx b/x-pack/plugins/triggers_actions_ui/public/application/home.tsx index 4d0a9980f2231f..b5f3b63c58a93a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/home.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/home.tsx @@ -167,3 +167,6 @@ export const TriggersActionsUIHome: React.FunctionComponent ); }; + +// eslint-disable-next-line import/no-default-export +export { TriggersActionsUIHome as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.tsx index 9198607df7863e..0caa880c4df00f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.tsx @@ -118,6 +118,6 @@ export async function getAlertData( } } -export const AlertDetailsRouteWithApi = withActionOperations( - withBulkAlertOperations(AlertDetailsRoute) -); +const AlertDetailsRouteWithApi = withActionOperations(withBulkAlertOperations(AlertDetailsRoute)); +// eslint-disable-next-line import/no-default-export +export { AlertDetailsRouteWithApi as default }; From f1ffe648d911043807abfc35c0d87ff9bdd0df48 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Wed, 6 May 2020 10:10:07 +0100 Subject: [PATCH 07/22] revert siem changes --- .../siem/public/lib/connectors/jira/flyout.tsx | 3 --- .../siem/public/lib/connectors/jira/index.tsx | 4 ++-- .../public/lib/connectors/servicenow/flyout.tsx | 3 --- .../public/lib/connectors/servicenow/index.tsx | 5 +++-- x-pack/plugins/siem/public/lib/connectors/types.ts | 3 +-- x-pack/plugins/siem/public/lib/connectors/utils.ts | 12 +++++++++++- x-pack/plugins/triggers_actions_ui/public/types.ts | 14 ++++++++------ 7 files changed, 25 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/siem/public/lib/connectors/jira/flyout.tsx b/x-pack/plugins/siem/public/lib/connectors/jira/flyout.tsx index 337ca2e3c918e0..9c3d1c90e67d7a 100644 --- a/x-pack/plugins/siem/public/lib/connectors/jira/flyout.tsx +++ b/x-pack/plugins/siem/public/lib/connectors/jira/flyout.tsx @@ -109,6 +109,3 @@ export const JiraConnectorFlyout = withConnectorFlyout({ configKeys: ['projectKey'], connectorActionTypeId: '.jira', }); - -// eslint-disable-next-line import/no-default-export -export { JiraConnectorFlyout as default }; diff --git a/x-pack/plugins/siem/public/lib/connectors/jira/index.tsx b/x-pack/plugins/siem/public/lib/connectors/jira/index.tsx index 049ccb7cf17b7e..ada9608e37c983 100644 --- a/x-pack/plugins/siem/public/lib/connectors/jira/index.tsx +++ b/x-pack/plugins/siem/public/lib/connectors/jira/index.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { lazy } from 'react'; import { ValidationResult, // eslint-disable-next-line @kbn/eslint/no-restricted-paths @@ -14,6 +13,7 @@ import { connector } from './config'; import { createActionType } from '../utils'; import logo from './logo.svg'; import { JiraActionConnector } from './types'; +import { JiraConnectorFlyout } from './flyout'; import * as i18n from './translations'; interface Errors { @@ -50,5 +50,5 @@ export const getActionType = createActionType({ selectMessage: i18n.JIRA_DESC, actionTypeTitle: connector.name, validateConnector, - actionConnectorFields: lazy(() => import('./flyout')), + actionConnectorFields: JiraConnectorFlyout, }); diff --git a/x-pack/plugins/siem/public/lib/connectors/servicenow/flyout.tsx b/x-pack/plugins/siem/public/lib/connectors/servicenow/flyout.tsx index 2783e988a64052..5d5d08dacf90c9 100644 --- a/x-pack/plugins/siem/public/lib/connectors/servicenow/flyout.tsx +++ b/x-pack/plugins/siem/public/lib/connectors/servicenow/flyout.tsx @@ -82,6 +82,3 @@ export const ServiceNowConnectorFlyout = withConnectorFlyout import('./flyout')), + actionConnectorFields: ServiceNowConnectorFlyout, }); diff --git a/x-pack/plugins/siem/public/lib/connectors/types.ts b/x-pack/plugins/siem/public/lib/connectors/types.ts index 3d3692c9806e48..ffb013c347e59e 100644 --- a/x-pack/plugins/siem/public/lib/connectors/types.ts +++ b/x-pack/plugins/siem/public/lib/connectors/types.ts @@ -8,7 +8,6 @@ /* eslint-disable @kbn/eslint/no-restricted-paths */ import { ActionType } from '../../../../triggers_actions_ui/public'; -import { IErrorObject } from '../../../../triggers_actions_ui/public/types'; import { ExternalIncidentServiceConfiguration } from '../../../../actions/server/builtin_action_types/case/types'; import { ActionType as ThirdPartySupportedActions, CaseField } from '../../../../case/common/api'; @@ -43,7 +42,7 @@ export interface ActionConnectorValidationErrors { export type Optional = Omit & Partial; export interface ConnectorFlyoutFormProps { - errors: IErrorObject; + errors: { [key: string]: string[] }; action: T; onChangeSecret: (key: string, value: string) => void; onBlurSecret: (key: string) => void; diff --git a/x-pack/plugins/siem/public/lib/connectors/utils.ts b/x-pack/plugins/siem/public/lib/connectors/utils.ts index cc1608a05e2ce8..169b4758876e8c 100644 --- a/x-pack/plugins/siem/public/lib/connectors/utils.ts +++ b/x-pack/plugins/siem/public/lib/connectors/utils.ts @@ -7,6 +7,7 @@ import { ActionTypeModel, ValidationResult, + ActionParamsProps, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../triggers_actions_ui/public/types'; @@ -30,7 +31,7 @@ export const createActionType = ({ validateConnector, validateParams = connectorParamsValidator, actionConnectorFields, - actionParamsFields = null, + actionParamsFields = ConnectorParamsFields, }: Optional) => (): ActionTypeModel => { return { id, @@ -58,6 +59,15 @@ export const createActionType = ({ }; }; +const ConnectorParamsFields: React.FunctionComponent> = ({ + actionParams, + editAction, + index, + errors, +}) => { + return null; +}; + const connectorParamsValidator = (actionParams: ActionConnectorParams): ValidationResult => { return { errors: {} }; }; diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index b65ed4927b03e3..b525078eef0417 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -54,12 +54,14 @@ export interface ActionTypeModel { actionTypeTitle?: string; validateConnector: (connector: any) => ValidationResult; validateParams: (actionParams: any) => ValidationResult; - actionConnectorFields: React.LazyExoticComponent< - ComponentType> - > | null; - actionParamsFields: React.LazyExoticComponent< - ComponentType> - > | null; + actionConnectorFields: + | React.FunctionComponent + | React.LazyExoticComponent>> + | null; + actionParamsFields: + | React.FunctionComponent + | React.LazyExoticComponent>> + | null; } export interface ValidationResult { From 7f95f905fac97249c19cfdcba6e36d1649ed4235 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Wed, 6 May 2020 10:17:40 +0100 Subject: [PATCH 08/22] corrected type in siem --- x-pack/plugins/siem/public/lib/connectors/types.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/siem/public/lib/connectors/types.ts b/x-pack/plugins/siem/public/lib/connectors/types.ts index ffb013c347e59e..3d3692c9806e48 100644 --- a/x-pack/plugins/siem/public/lib/connectors/types.ts +++ b/x-pack/plugins/siem/public/lib/connectors/types.ts @@ -8,6 +8,7 @@ /* eslint-disable @kbn/eslint/no-restricted-paths */ import { ActionType } from '../../../../triggers_actions_ui/public'; +import { IErrorObject } from '../../../../triggers_actions_ui/public/types'; import { ExternalIncidentServiceConfiguration } from '../../../../actions/server/builtin_action_types/case/types'; import { ActionType as ThirdPartySupportedActions, CaseField } from '../../../../case/common/api'; @@ -42,7 +43,7 @@ export interface ActionConnectorValidationErrors { export type Optional = Omit & Partial; export interface ConnectorFlyoutFormProps { - errors: { [key: string]: string[] }; + errors: IErrorObject; action: T; onChangeSecret: (key: string, value: string) => void; onBlurSecret: (key: string) => void; From 12e8da43c3baa0e5fc09b9c84015b6364af4540c Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Wed, 6 May 2020 15:03:01 +0100 Subject: [PATCH 09/22] Revert "revert siem changes" This reverts commit f1ffe648d911043807abfc35c0d87ff9bdd0df48. --- .../siem/public/lib/connectors/jira/flyout.tsx | 3 +++ .../siem/public/lib/connectors/jira/index.tsx | 4 ++-- .../public/lib/connectors/servicenow/flyout.tsx | 3 +++ .../public/lib/connectors/servicenow/index.tsx | 5 ++--- x-pack/plugins/siem/public/lib/connectors/utils.ts | 12 +----------- x-pack/plugins/triggers_actions_ui/public/types.ts | 14 ++++++-------- 6 files changed, 17 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/siem/public/lib/connectors/jira/flyout.tsx b/x-pack/plugins/siem/public/lib/connectors/jira/flyout.tsx index 9c3d1c90e67d7a..337ca2e3c918e0 100644 --- a/x-pack/plugins/siem/public/lib/connectors/jira/flyout.tsx +++ b/x-pack/plugins/siem/public/lib/connectors/jira/flyout.tsx @@ -109,3 +109,6 @@ export const JiraConnectorFlyout = withConnectorFlyout({ configKeys: ['projectKey'], connectorActionTypeId: '.jira', }); + +// eslint-disable-next-line import/no-default-export +export { JiraConnectorFlyout as default }; diff --git a/x-pack/plugins/siem/public/lib/connectors/jira/index.tsx b/x-pack/plugins/siem/public/lib/connectors/jira/index.tsx index ada9608e37c983..049ccb7cf17b7e 100644 --- a/x-pack/plugins/siem/public/lib/connectors/jira/index.tsx +++ b/x-pack/plugins/siem/public/lib/connectors/jira/index.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { lazy } from 'react'; import { ValidationResult, // eslint-disable-next-line @kbn/eslint/no-restricted-paths @@ -13,7 +14,6 @@ import { connector } from './config'; import { createActionType } from '../utils'; import logo from './logo.svg'; import { JiraActionConnector } from './types'; -import { JiraConnectorFlyout } from './flyout'; import * as i18n from './translations'; interface Errors { @@ -50,5 +50,5 @@ export const getActionType = createActionType({ selectMessage: i18n.JIRA_DESC, actionTypeTitle: connector.name, validateConnector, - actionConnectorFields: JiraConnectorFlyout, + actionConnectorFields: lazy(() => import('./flyout')), }); diff --git a/x-pack/plugins/siem/public/lib/connectors/servicenow/flyout.tsx b/x-pack/plugins/siem/public/lib/connectors/servicenow/flyout.tsx index 5d5d08dacf90c9..2783e988a64052 100644 --- a/x-pack/plugins/siem/public/lib/connectors/servicenow/flyout.tsx +++ b/x-pack/plugins/siem/public/lib/connectors/servicenow/flyout.tsx @@ -82,3 +82,6 @@ export const ServiceNowConnectorFlyout = withConnectorFlyout import('./flyout')), }); diff --git a/x-pack/plugins/siem/public/lib/connectors/utils.ts b/x-pack/plugins/siem/public/lib/connectors/utils.ts index 169b4758876e8c..cc1608a05e2ce8 100644 --- a/x-pack/plugins/siem/public/lib/connectors/utils.ts +++ b/x-pack/plugins/siem/public/lib/connectors/utils.ts @@ -7,7 +7,6 @@ import { ActionTypeModel, ValidationResult, - ActionParamsProps, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../triggers_actions_ui/public/types'; @@ -31,7 +30,7 @@ export const createActionType = ({ validateConnector, validateParams = connectorParamsValidator, actionConnectorFields, - actionParamsFields = ConnectorParamsFields, + actionParamsFields = null, }: Optional) => (): ActionTypeModel => { return { id, @@ -59,15 +58,6 @@ export const createActionType = ({ }; }; -const ConnectorParamsFields: React.FunctionComponent> = ({ - actionParams, - editAction, - index, - errors, -}) => { - return null; -}; - const connectorParamsValidator = (actionParams: ActionConnectorParams): ValidationResult => { return { errors: {} }; }; diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index b525078eef0417..b65ed4927b03e3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -54,14 +54,12 @@ export interface ActionTypeModel { actionTypeTitle?: string; validateConnector: (connector: any) => ValidationResult; validateParams: (actionParams: any) => ValidationResult; - actionConnectorFields: - | React.FunctionComponent - | React.LazyExoticComponent>> - | null; - actionParamsFields: - | React.FunctionComponent - | React.LazyExoticComponent>> - | null; + actionConnectorFields: React.LazyExoticComponent< + ComponentType> + > | null; + actionParamsFields: React.LazyExoticComponent< + ComponentType> + > | null; } export interface ValidationResult { From 61b1b0f7b13a397b2b1a1f650798943792557a3b Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 7 May 2020 09:49:35 +0100 Subject: [PATCH 10/22] updated docs --- x-pack/plugins/triggers_actions_ui/README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/README.md b/x-pack/plugins/triggers_actions_ui/README.md index ece1791c66e115..c5f02863ba8a19 100644 --- a/x-pack/plugins/triggers_actions_ui/README.md +++ b/x-pack/plugins/triggers_actions_ui/README.md @@ -985,8 +985,8 @@ Each action type should be defined as an `ActionTypeModel` object with the follo |selectMessage|Short description of action type responsibility, that will be displayed on the select card in UI.| |validateConnector|Validation function for action connector.| |validateParams|Validation function for action params.| -|actionConnectorFields|React functional component for building UI of current action type connector.| -|actionParamsFields|React functional component for building UI of current action type params. Displayed as a part of Create Alert flyout.| +|actionConnectorFields|A lazy loaded React component for building UI of current action type connector.| +|actionParamsFields|A lazy loaded React component for building UI of current action type params. Displayed as a part of Create Alert flyout.| ## Register action type model @@ -1082,8 +1082,8 @@ export function getActionType(): ActionTypeModel { } return validationResult; }, - actionConnectorFields: ExampleConnectorFields, - actionParamsFields: ExampleParamsFields, + actionConnectorFields: lazy(() => import('./example_connector_fields')), + actionParamsFields: lazy(() => import('./example_params_fields')), }; } ``` @@ -1130,6 +1130,9 @@ const ExampleConnectorFields: React.FunctionComponent ); }; + +// Export as default in order to support lazy loading +export {ExampleConnectorFields as default}; ``` 3. Define action type params fields using the property of `ActionTypeModel` `actionParamsFields`: @@ -1175,6 +1178,9 @@ const ExampleParamsFields: React.FunctionComponent ); }; + +// Export as default in order to support lazy loading +export {ExampleParamsFields as default}; ``` 4. Extend registration code with the new action type register in the file `x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts` From 105a802bdb7306d70470527bfd2ea23bb2028a3e Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 7 May 2020 10:33:18 +0100 Subject: [PATCH 11/22] fixed missing docLinks --- .../builtin_action_types/email/email_connector.test.tsx | 2 ++ .../builtin_action_types/webhook/webhook_connectors.test.tsx | 2 ++ 2 files changed, 4 insertions(+) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx index bddd49fb65befc..67514e815bc494 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { EmailActionConnector } from '../types'; import EmailActionConnectorFields from './email_connector'; +import { DocLinksStart } from 'kibana/public'; describe('EmailActionConnectorFields renders', () => { test('all connector fields is rendered', () => { @@ -28,6 +29,7 @@ describe('EmailActionConnectorFields renders', () => { errors={{ from: [], port: [], host: [], user: [], password: [] }} editActionConfig={() => {}} editActionSecrets={() => {}} + docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart} /> ); expect(wrapper.find('[data-test-subj="emailFromInput"]').length > 0).toBeTruthy(); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx index 6f6c9d7f095317..842ec517853551 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { WebhookActionConnector } from '../types'; import WebhookActionConnectorFields from './webhook_connectors'; +import { DocLinksStart } from 'kibana/public'; describe('WebhookActionConnectorFields renders', () => { test('all connector fields is rendered', () => { @@ -31,6 +32,7 @@ describe('WebhookActionConnectorFields renders', () => { errors={{ url: [], method: [], user: [], password: [] }} editActionConfig={() => {}} editActionSecrets={() => {}} + docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart} /> ); expect(wrapper.find('[data-test-subj="webhookViewHeadersSwitch"]').length > 0).toBeTruthy(); From 52809758964e4eadfe14c12ac24ae824fcc897a0 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 7 May 2020 11:07:15 +0100 Subject: [PATCH 12/22] lazy load exposed flyout components --- .../public/application/app.tsx | 21 +++++++------------ .../lib/suspended_component_with_props.tsx | 17 +++++++++++++++ .../action_connector_form/action_form.tsx | 3 +++ .../connector_add_flyout.tsx | 3 +++ .../connector_edit_flyout.tsx | 3 +++ .../components/alert_details.tsx | 2 +- .../sections/alert_form/alert_add.tsx | 3 +++ .../sections/alert_form/alert_edit.tsx | 3 +++ .../application/sections/alert_form/index.ts | 8 ------- .../alerts_list/components/alerts_list.tsx | 2 +- .../public/application/sections/index.tsx | 21 +++++++++++++++++++ .../triggers_actions_ui/public/index.ts | 6 +++--- 12 files changed, 65 insertions(+), 27 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx index 327b148fa3e466..58466d76f860d6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx @@ -3,7 +3,7 @@ * 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, { lazy, Suspense } from 'react'; +import React, { lazy } from 'react'; import { Switch, Route, Redirect, HashRouter, RouteComponentProps } from 'react-router-dom'; import { ChromeStart, @@ -15,7 +15,6 @@ import { ChromeBreadcrumb, CoreStart, } from 'kibana/public'; -import { EuiLoadingSpinner } from '@elastic/eui'; import { BASE_PATH, Section, routeToAlertDetails } from './constants'; import { AppContextProvider, useAppDependencies } from './app_context'; import { hasShowAlertsCapability } from './lib/capabilities'; @@ -24,6 +23,7 @@ import { TypeRegistry } from './type_registry'; import { ChartsPluginStart } from '../../../../../src/plugins/charts/public'; import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; import { PluginStartContract as AlertingStart } from '../../../alerting/public'; +import { suspendedComponentWithProps } from './lib/suspended_component_with_props'; const TriggersActionsUIHome = lazy(() => import('./home')); const AlertDetailsRoute = lazy(() => @@ -68,22 +68,15 @@ export const AppWithoutRouter = ({ sectionsRegex }: { sectionsRegex: string }) = >(TriggersActionsUIHome)} /> {canShowAlerts && ( - + >(AlertDetailsRoute)} + /> )} ); }; - -function suspendedRouteComponent( - RouteComponent: React.ComponentType> -) { - return (props: RouteComponentProps) => ( - }> - - - ); -} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx b/x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx new file mode 100644 index 00000000000000..8d6264a89637c9 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx @@ -0,0 +1,17 @@ +/* + * 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, { Suspense } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; + +export function suspendedComponentWithProps( + ComponentToSuspend: React.ComponentType +) { + return (props: T) => ( + }> + + + ); +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx index 300c5985db473d..7d1ec1415d92f0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx @@ -699,3 +699,6 @@ export const ActionForm = ({ ); }; + +// eslint-disable-next-line import/no-default-export +export { ActionForm as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx index c9844f4e10864b..adee2e09a56fdc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx @@ -319,3 +319,6 @@ const UpgradeYourLicenseCallOut = ({ http }: { http: HttpSetup }) => ( ); + +// eslint-disable-next-line import/no-default-export +export { ConnectorAddFlyout as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx index 4a0effcbd68254..6ea78f60c52ea0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx @@ -254,3 +254,6 @@ export const ConnectorEditFlyout = ({ ); }; + +// eslint-disable-next-line import/no-default-export +export { ConnectorEditFlyout as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx index 3440bb28b24684..18ec3e64003e62 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx @@ -37,9 +37,9 @@ import { import { AlertInstancesRouteWithApi } from './alert_instances_route'; import { ViewInApp } from './view_in_app'; import { PLUGIN } from '../../../constants/plugin'; -import { AlertEdit } from '../../alert_form'; import { AlertsContextProvider } from '../../../context/alerts_context'; import { routeToAlertDetails } from '../../../constants'; +import { AlertEdit } from '../../../sections'; type AlertDetailsProps = { alert: Alert; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx index 651f2cdba34af7..004ad97083fe4b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx @@ -219,3 +219,6 @@ const parseErrors: (errors: IErrorObject) => boolean = errors => if (isObject(errorList)) return parseErrors(errorList as IErrorObject); return errorList.length >= 1; }); + +// eslint-disable-next-line import/no-default-export +export { AlertAdd as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx index 00bc9874face19..92c1a04f7c1807 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx @@ -210,3 +210,6 @@ export const AlertEdit = ({ ); }; + +// eslint-disable-next-line import/no-default-export +export { AlertEdit as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.ts deleted file mode 100644 index 83ed9671238b16..00000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * 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 { AlertAdd } from './alert_add'; -export { AlertEdit } from './alert_edit'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index fa83176a143443..b30448a1c1d691 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -25,7 +25,7 @@ import { isEmpty } from 'lodash'; import { AlertsContextProvider } from '../../../context/alerts_context'; import { useAppDependencies } from '../../../app_context'; import { ActionType, Alert, AlertTableItem, AlertTypeIndex, Pagination } from '../../../../types'; -import { AlertAdd } from '../../alert_form'; +import { AlertAdd } from '../../../sections'; import { BulkOperationPopover } from '../../common/components/bulk_operation_popover'; import { AlertQuickEditButtonsWithApi as AlertQuickEditButtons } from '../../common/components/alert_quick_edit_buttons'; import { CollapsedItemActionsWithApi as CollapsedItemActions } from './collapsed_item_actions'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx new file mode 100644 index 00000000000000..677ee139271c0d --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx @@ -0,0 +1,21 @@ +/* + * 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 { lazy } from 'react'; +import { suspendedComponentWithProps } from '../lib/suspended_component_with_props'; + +export const AlertAdd = suspendedComponentWithProps(lazy(() => import('./alert_form/alert_add'))); +export const AlertEdit = suspendedComponentWithProps(lazy(() => import('./alert_form/alert_edit'))); + +export const ConnectorAddFlyout = suspendedComponentWithProps( + lazy(() => import('./action_connector_form/connector_add_flyout')) +); +export const ConnectorEditFlyout = suspendedComponentWithProps( + lazy(() => import('./action_connector_form/connector_edit_flyout')) +); +export const ActionForm = suspendedComponentWithProps( + lazy(() => import('./action_connector_form/action_form')) +); diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index 96645e856e4183..b2f89658b18af8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -9,13 +9,13 @@ import { Plugin } from './plugin'; export { AlertsContextProvider } from './application/context/alerts_context'; export { ActionsConnectorsContextProvider } from './application/context/actions_connectors_context'; -export { AlertAdd } from './application/sections/alert_form'; -export { ActionForm } from './application/sections/action_connector_form'; export { AlertAction, Alert, AlertTypeModel, ActionType } from './types'; export { + ActionForm, + AlertAdd, ConnectorAddFlyout, ConnectorEditFlyout, -} from './application/sections/action_connector_form'; +} from './application/sections'; export function plugin(ctx: PluginInitializerContext) { return new Plugin(ctx); From 6172ab12ebf7b5ca2e8fb3a23aa0fb6aae9a642e Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 7 May 2020 11:27:09 +0100 Subject: [PATCH 13/22] Revert "lazy load exposed flyout components" This reverts commit 52809758964e4eadfe14c12ac24ae824fcc897a0. --- .../public/application/app.tsx | 21 ++++++++++++------- .../lib/suspended_component_with_props.tsx | 17 --------------- .../action_connector_form/action_form.tsx | 3 --- .../connector_add_flyout.tsx | 3 --- .../connector_edit_flyout.tsx | 3 --- .../components/alert_details.tsx | 2 +- .../sections/alert_form/alert_add.tsx | 3 --- .../sections/alert_form/alert_edit.tsx | 3 --- .../application/sections/alert_form/index.ts | 8 +++++++ .../alerts_list/components/alerts_list.tsx | 2 +- .../public/application/sections/index.tsx | 21 ------------------- .../triggers_actions_ui/public/index.ts | 6 +++--- 12 files changed, 27 insertions(+), 65 deletions(-) delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.ts delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx index 58466d76f860d6..327b148fa3e466 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx @@ -3,7 +3,7 @@ * 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, { lazy } from 'react'; +import React, { lazy, Suspense } from 'react'; import { Switch, Route, Redirect, HashRouter, RouteComponentProps } from 'react-router-dom'; import { ChromeStart, @@ -15,6 +15,7 @@ import { ChromeBreadcrumb, CoreStart, } from 'kibana/public'; +import { EuiLoadingSpinner } from '@elastic/eui'; import { BASE_PATH, Section, routeToAlertDetails } from './constants'; import { AppContextProvider, useAppDependencies } from './app_context'; import { hasShowAlertsCapability } from './lib/capabilities'; @@ -23,7 +24,6 @@ import { TypeRegistry } from './type_registry'; import { ChartsPluginStart } from '../../../../../src/plugins/charts/public'; import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; import { PluginStartContract as AlertingStart } from '../../../alerting/public'; -import { suspendedComponentWithProps } from './lib/suspended_component_with_props'; const TriggersActionsUIHome = lazy(() => import('./home')); const AlertDetailsRoute = lazy(() => @@ -68,15 +68,22 @@ export const AppWithoutRouter = ({ sectionsRegex }: { sectionsRegex: string }) = >(TriggersActionsUIHome)} + component={suspendedRouteComponent(TriggersActionsUIHome)} /> {canShowAlerts && ( - >(AlertDetailsRoute)} - /> + )} ); }; + +function suspendedRouteComponent( + RouteComponent: React.ComponentType> +) { + return (props: RouteComponentProps) => ( + }> + + + ); +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx b/x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx deleted file mode 100644 index 8d6264a89637c9..00000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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, { Suspense } from 'react'; -import { EuiLoadingSpinner } from '@elastic/eui'; - -export function suspendedComponentWithProps( - ComponentToSuspend: React.ComponentType -) { - return (props: T) => ( - }> - - - ); -} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx index 7d1ec1415d92f0..300c5985db473d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx @@ -699,6 +699,3 @@ export const ActionForm = ({ ); }; - -// eslint-disable-next-line import/no-default-export -export { ActionForm as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx index adee2e09a56fdc..c9844f4e10864b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx @@ -319,6 +319,3 @@ const UpgradeYourLicenseCallOut = ({ http }: { http: HttpSetup }) => ( ); - -// eslint-disable-next-line import/no-default-export -export { ConnectorAddFlyout as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx index 6ea78f60c52ea0..4a0effcbd68254 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx @@ -254,6 +254,3 @@ export const ConnectorEditFlyout = ({ ); }; - -// eslint-disable-next-line import/no-default-export -export { ConnectorEditFlyout as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx index 18ec3e64003e62..3440bb28b24684 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx @@ -37,9 +37,9 @@ import { import { AlertInstancesRouteWithApi } from './alert_instances_route'; import { ViewInApp } from './view_in_app'; import { PLUGIN } from '../../../constants/plugin'; +import { AlertEdit } from '../../alert_form'; import { AlertsContextProvider } from '../../../context/alerts_context'; import { routeToAlertDetails } from '../../../constants'; -import { AlertEdit } from '../../../sections'; type AlertDetailsProps = { alert: Alert; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx index 004ad97083fe4b..651f2cdba34af7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx @@ -219,6 +219,3 @@ const parseErrors: (errors: IErrorObject) => boolean = errors => if (isObject(errorList)) return parseErrors(errorList as IErrorObject); return errorList.length >= 1; }); - -// eslint-disable-next-line import/no-default-export -export { AlertAdd as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx index 92c1a04f7c1807..00bc9874face19 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx @@ -210,6 +210,3 @@ export const AlertEdit = ({ ); }; - -// eslint-disable-next-line import/no-default-export -export { AlertEdit as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.ts new file mode 100644 index 00000000000000..83ed9671238b16 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.ts @@ -0,0 +1,8 @@ +/* + * 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 { AlertAdd } from './alert_add'; +export { AlertEdit } from './alert_edit'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index b30448a1c1d691..fa83176a143443 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -25,7 +25,7 @@ import { isEmpty } from 'lodash'; import { AlertsContextProvider } from '../../../context/alerts_context'; import { useAppDependencies } from '../../../app_context'; import { ActionType, Alert, AlertTableItem, AlertTypeIndex, Pagination } from '../../../../types'; -import { AlertAdd } from '../../../sections'; +import { AlertAdd } from '../../alert_form'; import { BulkOperationPopover } from '../../common/components/bulk_operation_popover'; import { AlertQuickEditButtonsWithApi as AlertQuickEditButtons } from '../../common/components/alert_quick_edit_buttons'; import { CollapsedItemActionsWithApi as CollapsedItemActions } from './collapsed_item_actions'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx deleted file mode 100644 index 677ee139271c0d..00000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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 { lazy } from 'react'; -import { suspendedComponentWithProps } from '../lib/suspended_component_with_props'; - -export const AlertAdd = suspendedComponentWithProps(lazy(() => import('./alert_form/alert_add'))); -export const AlertEdit = suspendedComponentWithProps(lazy(() => import('./alert_form/alert_edit'))); - -export const ConnectorAddFlyout = suspendedComponentWithProps( - lazy(() => import('./action_connector_form/connector_add_flyout')) -); -export const ConnectorEditFlyout = suspendedComponentWithProps( - lazy(() => import('./action_connector_form/connector_edit_flyout')) -); -export const ActionForm = suspendedComponentWithProps( - lazy(() => import('./action_connector_form/action_form')) -); diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index b2f89658b18af8..96645e856e4183 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -9,13 +9,13 @@ import { Plugin } from './plugin'; export { AlertsContextProvider } from './application/context/alerts_context'; export { ActionsConnectorsContextProvider } from './application/context/actions_connectors_context'; +export { AlertAdd } from './application/sections/alert_form'; +export { ActionForm } from './application/sections/action_connector_form'; export { AlertAction, Alert, AlertTypeModel, ActionType } from './types'; export { - ActionForm, - AlertAdd, ConnectorAddFlyout, ConnectorEditFlyout, -} from './application/sections'; +} from './application/sections/action_connector_form'; export function plugin(ctx: PluginInitializerContext) { return new Plugin(ctx); From b89934b9330dbfdc1c54e264f425343658523918 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 7 May 2020 13:59:50 +0100 Subject: [PATCH 14/22] Revert "Revert "lazy load exposed flyout components"" This reverts commit 6172ab12ebf7b5ca2e8fb3a23aa0fb6aae9a642e. --- .../public/application/app.tsx | 21 +++++++------------ .../lib/suspended_component_with_props.tsx | 17 +++++++++++++++ .../action_connector_form/action_form.tsx | 3 +++ .../connector_add_flyout.tsx | 3 +++ .../connector_edit_flyout.tsx | 3 +++ .../components/alert_details.tsx | 2 +- .../sections/alert_form/alert_add.tsx | 3 +++ .../sections/alert_form/alert_edit.tsx | 3 +++ .../application/sections/alert_form/index.ts | 8 ------- .../alerts_list/components/alerts_list.tsx | 2 +- .../public/application/sections/index.tsx | 21 +++++++++++++++++++ .../triggers_actions_ui/public/index.ts | 6 +++--- 12 files changed, 65 insertions(+), 27 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx index 327b148fa3e466..58466d76f860d6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx @@ -3,7 +3,7 @@ * 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, { lazy, Suspense } from 'react'; +import React, { lazy } from 'react'; import { Switch, Route, Redirect, HashRouter, RouteComponentProps } from 'react-router-dom'; import { ChromeStart, @@ -15,7 +15,6 @@ import { ChromeBreadcrumb, CoreStart, } from 'kibana/public'; -import { EuiLoadingSpinner } from '@elastic/eui'; import { BASE_PATH, Section, routeToAlertDetails } from './constants'; import { AppContextProvider, useAppDependencies } from './app_context'; import { hasShowAlertsCapability } from './lib/capabilities'; @@ -24,6 +23,7 @@ import { TypeRegistry } from './type_registry'; import { ChartsPluginStart } from '../../../../../src/plugins/charts/public'; import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; import { PluginStartContract as AlertingStart } from '../../../alerting/public'; +import { suspendedComponentWithProps } from './lib/suspended_component_with_props'; const TriggersActionsUIHome = lazy(() => import('./home')); const AlertDetailsRoute = lazy(() => @@ -68,22 +68,15 @@ export const AppWithoutRouter = ({ sectionsRegex }: { sectionsRegex: string }) = >(TriggersActionsUIHome)} /> {canShowAlerts && ( - + >(AlertDetailsRoute)} + /> )} ); }; - -function suspendedRouteComponent( - RouteComponent: React.ComponentType> -) { - return (props: RouteComponentProps) => ( - }> - - - ); -} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx b/x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx new file mode 100644 index 00000000000000..8d6264a89637c9 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx @@ -0,0 +1,17 @@ +/* + * 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, { Suspense } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; + +export function suspendedComponentWithProps( + ComponentToSuspend: React.ComponentType +) { + return (props: T) => ( + }> + + + ); +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx index 300c5985db473d..7d1ec1415d92f0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx @@ -699,3 +699,6 @@ export const ActionForm = ({ ); }; + +// eslint-disable-next-line import/no-default-export +export { ActionForm as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx index c9844f4e10864b..adee2e09a56fdc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx @@ -319,3 +319,6 @@ const UpgradeYourLicenseCallOut = ({ http }: { http: HttpSetup }) => ( ); + +// eslint-disable-next-line import/no-default-export +export { ConnectorAddFlyout as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx index 4a0effcbd68254..6ea78f60c52ea0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx @@ -254,3 +254,6 @@ export const ConnectorEditFlyout = ({ ); }; + +// eslint-disable-next-line import/no-default-export +export { ConnectorEditFlyout as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx index 3440bb28b24684..18ec3e64003e62 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx @@ -37,9 +37,9 @@ import { import { AlertInstancesRouteWithApi } from './alert_instances_route'; import { ViewInApp } from './view_in_app'; import { PLUGIN } from '../../../constants/plugin'; -import { AlertEdit } from '../../alert_form'; import { AlertsContextProvider } from '../../../context/alerts_context'; import { routeToAlertDetails } from '../../../constants'; +import { AlertEdit } from '../../../sections'; type AlertDetailsProps = { alert: Alert; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx index 651f2cdba34af7..004ad97083fe4b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx @@ -219,3 +219,6 @@ const parseErrors: (errors: IErrorObject) => boolean = errors => if (isObject(errorList)) return parseErrors(errorList as IErrorObject); return errorList.length >= 1; }); + +// eslint-disable-next-line import/no-default-export +export { AlertAdd as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx index 00bc9874face19..92c1a04f7c1807 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx @@ -210,3 +210,6 @@ export const AlertEdit = ({ ); }; + +// eslint-disable-next-line import/no-default-export +export { AlertEdit as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.ts deleted file mode 100644 index 83ed9671238b16..00000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * 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 { AlertAdd } from './alert_add'; -export { AlertEdit } from './alert_edit'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index fa83176a143443..b30448a1c1d691 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -25,7 +25,7 @@ import { isEmpty } from 'lodash'; import { AlertsContextProvider } from '../../../context/alerts_context'; import { useAppDependencies } from '../../../app_context'; import { ActionType, Alert, AlertTableItem, AlertTypeIndex, Pagination } from '../../../../types'; -import { AlertAdd } from '../../alert_form'; +import { AlertAdd } from '../../../sections'; import { BulkOperationPopover } from '../../common/components/bulk_operation_popover'; import { AlertQuickEditButtonsWithApi as AlertQuickEditButtons } from '../../common/components/alert_quick_edit_buttons'; import { CollapsedItemActionsWithApi as CollapsedItemActions } from './collapsed_item_actions'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx new file mode 100644 index 00000000000000..677ee139271c0d --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx @@ -0,0 +1,21 @@ +/* + * 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 { lazy } from 'react'; +import { suspendedComponentWithProps } from '../lib/suspended_component_with_props'; + +export const AlertAdd = suspendedComponentWithProps(lazy(() => import('./alert_form/alert_add'))); +export const AlertEdit = suspendedComponentWithProps(lazy(() => import('./alert_form/alert_edit'))); + +export const ConnectorAddFlyout = suspendedComponentWithProps( + lazy(() => import('./action_connector_form/connector_add_flyout')) +); +export const ConnectorEditFlyout = suspendedComponentWithProps( + lazy(() => import('./action_connector_form/connector_edit_flyout')) +); +export const ActionForm = suspendedComponentWithProps( + lazy(() => import('./action_connector_form/action_form')) +); diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index 96645e856e4183..b2f89658b18af8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -9,13 +9,13 @@ import { Plugin } from './plugin'; export { AlertsContextProvider } from './application/context/alerts_context'; export { ActionsConnectorsContextProvider } from './application/context/actions_connectors_context'; -export { AlertAdd } from './application/sections/alert_form'; -export { ActionForm } from './application/sections/action_connector_form'; export { AlertAction, Alert, AlertTypeModel, ActionType } from './types'; export { + ActionForm, + AlertAdd, ConnectorAddFlyout, ConnectorEditFlyout, -} from './application/sections/action_connector_form'; +} from './application/sections'; export function plugin(ctx: PluginInitializerContext) { return new Plugin(ctx); From f6774a16f8e4e25adf9ba7fe880b84ff71b7d148 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 7 May 2020 14:08:10 +0100 Subject: [PATCH 15/22] load exposed components lazily --- .../triggers_actions_ui/public/application/app.tsx | 7 ++++--- .../sections/action_connector_form/index.ts | 13 ++++++++++--- .../alert_details/components/alert_details.tsx | 2 +- .../application/sections/alert_form/alert_form.tsx | 2 +- .../application/sections/alert_form/index.tsx | 10 ++++++++++ .../sections/alerts_list/components/alerts_list.tsx | 2 +- x-pack/plugins/triggers_actions_ui/public/index.ts | 6 +++--- 7 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx index 58466d76f860d6..447959346ea693 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx @@ -3,7 +3,8 @@ * 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, { lazy } from 'react'; +import React, { lazy, Suspense } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; import { Switch, Route, Redirect, HashRouter, RouteComponentProps } from 'react-router-dom'; import { ChromeStart, @@ -68,12 +69,12 @@ export const AppWithoutRouter = ({ sectionsRegex }: { sectionsRegex: string }) = >(TriggersActionsUIHome)} + component={suspendedComponentWithProps(TriggersActionsUIHome)} /> {canShowAlerts && ( >(AlertDetailsRoute)} + component={suspendedComponentWithProps(AlertDetailsRoute)} /> )} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/index.ts index 52ee1efbdaf9fe..e0065c143a1a22 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/index.ts @@ -4,6 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -export { ConnectorAddFlyout } from './connector_add_flyout'; -export { ConnectorEditFlyout } from './connector_edit_flyout'; -export { ActionForm } from './action_form'; +import { lazy } from 'react'; +import { suspendedComponentWithProps } from '../../lib/suspended_component_with_props'; + +export const ConnectorAddFlyout = suspendedComponentWithProps( + lazy(() => import('./connector_add_flyout')) +); +export const ConnectorEditFlyout = suspendedComponentWithProps( + lazy(() => import('./connector_edit_flyout')) +); +export const ActionForm = suspendedComponentWithProps(lazy(() => import('./action_form'))); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx index 18ec3e64003e62..3440bb28b24684 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx @@ -37,9 +37,9 @@ import { import { AlertInstancesRouteWithApi } from './alert_instances_route'; import { ViewInApp } from './view_in_app'; import { PLUGIN } from '../../../constants/plugin'; +import { AlertEdit } from '../../alert_form'; import { AlertsContextProvider } from '../../../context/alerts_context'; import { routeToAlertDetails } from '../../../constants'; -import { AlertEdit } from '../../../sections'; type AlertDetailsProps = { alert: Alert; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx index 3b7283e69e0193..6d23a6fb66f353 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx @@ -36,7 +36,7 @@ import { AlertReducerAction } from './alert_reducer'; import { AlertTypeModel, Alert, IErrorObject, AlertAction, AlertTypeIndex } from '../../../types'; import { getTimeOptions } from '../../../common/lib/get_time_options'; import { useAlertsContext } from '../../context/alerts_context'; -import { ActionForm } from '../action_connector_form/action_form'; +import { ActionForm } from '../action_connector_form'; export function validateBaseProperties(alertObject: Alert) { const validationResult = { errors: {} }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.tsx new file mode 100644 index 00000000000000..79720edc4672ed --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.tsx @@ -0,0 +1,10 @@ +/* + * 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 { lazy } from 'react'; +import { suspendedComponentWithProps } from '../../lib/suspended_component_with_props'; + +export const AlertAdd = suspendedComponentWithProps(lazy(() => import('./alert_add'))); +export const AlertEdit = suspendedComponentWithProps(lazy(() => import('./alert_edit'))); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index b30448a1c1d691..fa83176a143443 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -25,7 +25,7 @@ import { isEmpty } from 'lodash'; import { AlertsContextProvider } from '../../../context/alerts_context'; import { useAppDependencies } from '../../../app_context'; import { ActionType, Alert, AlertTableItem, AlertTypeIndex, Pagination } from '../../../../types'; -import { AlertAdd } from '../../../sections'; +import { AlertAdd } from '../../alert_form'; import { BulkOperationPopover } from '../../common/components/bulk_operation_popover'; import { AlertQuickEditButtonsWithApi as AlertQuickEditButtons } from '../../common/components/alert_quick_edit_buttons'; import { CollapsedItemActionsWithApi as CollapsedItemActions } from './collapsed_item_actions'; diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index b2f89658b18af8..96645e856e4183 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -9,13 +9,13 @@ import { Plugin } from './plugin'; export { AlertsContextProvider } from './application/context/alerts_context'; export { ActionsConnectorsContextProvider } from './application/context/actions_connectors_context'; +export { AlertAdd } from './application/sections/alert_form'; +export { ActionForm } from './application/sections/action_connector_form'; export { AlertAction, Alert, AlertTypeModel, ActionType } from './types'; export { - ActionForm, - AlertAdd, ConnectorAddFlyout, ConnectorEditFlyout, -} from './application/sections'; +} from './application/sections/action_connector_form'; export function plugin(ctx: PluginInitializerContext) { return new Plugin(ctx); From 9a497b6b30a0cb661cdeb1a486c77c71dd908d14 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 7 May 2020 15:29:01 +0100 Subject: [PATCH 16/22] align and style loading indicator --- .../triggers_actions_ui/public/application/app.tsx | 14 +++++++++++--- .../components/builtin_action_types/email/index.ts | 7 +++++++ .../builtin_action_types/es_index/index.ts | 7 +++++++ .../components/builtin_action_types/index.ts | 12 ++++++------ .../builtin_action_types/pagerduty/index.ts | 7 +++++++ .../builtin_action_types/server_log/index.ts | 7 +++++++ .../components/builtin_action_types/slack/index.ts | 7 +++++++ .../builtin_action_types/webhook/index.ts | 7 +++++++ .../action_connector_form.tsx | 12 +++++++++++- .../sections/action_connector_form/action_form.tsx | 10 +++++++++- 10 files changed, 79 insertions(+), 11 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/index.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/index.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/index.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/index.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/index.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx index 327b148fa3e466..63860e062c8da8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx @@ -15,7 +15,7 @@ import { ChromeBreadcrumb, CoreStart, } from 'kibana/public'; -import { EuiLoadingSpinner } from '@elastic/eui'; +import { EuiLoadingSpinner, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { BASE_PATH, Section, routeToAlertDetails } from './constants'; import { AppContextProvider, useAppDependencies } from './app_context'; import { hasShowAlertsCapability } from './lib/capabilities'; @@ -25,7 +25,7 @@ import { ChartsPluginStart } from '../../../../../src/plugins/charts/public'; import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; import { PluginStartContract as AlertingStart } from '../../../alerting/public'; -const TriggersActionsUIHome = lazy(() => import('./home')); +const TriggersActionsUIHome = lazy(async () => import('./home')); const AlertDetailsRoute = lazy(() => import('./sections/alert_details/components/alert_details_route') ); @@ -82,7 +82,15 @@ function suspendedRouteComponent( RouteComponent: React.ComponentType> ) { return (props: RouteComponentProps) => ( - }> + + + + + + } + > ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.ts new file mode 100644 index 00000000000000..e0dd24a44aa8f3 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/index.ts @@ -0,0 +1,7 @@ +/* + * 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 { getActionType as getEmailActionType } from './email'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/index.ts new file mode 100644 index 00000000000000..6a2ebd9c4bc717 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/index.ts @@ -0,0 +1,7 @@ +/* + * 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 { getActionType as getIndexActionType } from './es_index'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts index 8d82b35f3fb7c8..8f49fa46dd54e6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getActionType as getServerLogActionType } from './server_log/server_log'; -import { getActionType as getSlackActionType } from './slack/slack'; -import { getActionType as getEmailActionType } from './email/email'; -import { getActionType as getIndexActionType } from './es_index/es_index'; -import { getActionType as getPagerDutyActionType } from './pagerduty/pagerduty'; -import { getActionType as getWebhookActionType } from './webhook/webhook'; +import { getServerLogActionType } from './server_log'; +import { getSlackActionType } from './slack'; +import { getEmailActionType } from './email'; +import { getIndexActionType } from './es_index'; +import { getPagerDutyActionType } from './pagerduty'; +import { getWebhookActionType } from './webhook'; import { TypeRegistry } from '../../type_registry'; import { ActionTypeModel } from '../../../types'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/index.ts new file mode 100644 index 00000000000000..9128ec81391ab3 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/index.ts @@ -0,0 +1,7 @@ +/* + * 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 { getActionType as getPagerDutyActionType } from './pagerduty'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/index.ts new file mode 100644 index 00000000000000..f85c7460d2ece1 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/index.ts @@ -0,0 +1,7 @@ +/* + * 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 { getActionType as getServerLogActionType } from './server_log'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/index.ts new file mode 100644 index 00000000000000..64ab6670754c92 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/index.ts @@ -0,0 +1,7 @@ +/* + * 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 { getActionType as getSlackActionType } from './slack'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/index.ts new file mode 100644 index 00000000000000..c43cab26b072e9 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/index.ts @@ -0,0 +1,7 @@ +/* + * 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 { getActionType as getWebhookActionType } from './webhook'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx index 7fb4d053b7f941..06ddce39567a40 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx @@ -13,6 +13,8 @@ import { EuiFieldText, EuiFormRow, EuiLoadingSpinner, + EuiFlexGroup, + EuiFlexItem, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -152,7 +154,15 @@ export const ActionConnectorForm = ({ {FieldsComponent !== null ? ( - }> + + + + + + } + > {ParamsFieldsComponent ? ( - }> + + + + + + } + > Date: Thu, 7 May 2020 15:45:08 +0100 Subject: [PATCH 17/22] spinner is configurable --- .../lib/suspended_component_with_props.tsx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx b/x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx index 8d6264a89637c9..563353793f991f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/suspended_component_with_props.tsx @@ -4,13 +4,23 @@ * you may not use this file except in compliance with the Elastic License. */ import React, { Suspense } from 'react'; -import { EuiLoadingSpinner } from '@elastic/eui'; +import { EuiLoadingSpinner, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiLoadingSpinnerSize } from '@elastic/eui/src/components/loading/loading_spinner'; export function suspendedComponentWithProps( - ComponentToSuspend: React.ComponentType + ComponentToSuspend: React.ComponentType, + size?: EuiLoadingSpinnerSize ) { return (props: T) => ( - }> + + + + + + } + > ); From 67674f109c6e9b2cf0777e926a001ec99d708297 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 11 May 2020 10:16:59 +0100 Subject: [PATCH 18/22] removed unused import --- .../public/application/app.tsx | 35 ++++--------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx index 24fdb8a39ae894..ebd9294ce1e6d8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx @@ -3,8 +3,8 @@ * 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, { lazy, Suspense } from 'react'; -import { Switch, Route, Redirect, HashRouter, RouteComponentProps } from 'react-router-dom'; +import React, { lazy } from 'react'; +import { Switch, Route, Redirect, HashRouter } from 'react-router-dom'; import { ChromeStart, DocLinksStart, @@ -15,7 +15,6 @@ import { ChromeBreadcrumb, CoreStart, } from 'kibana/public'; -import { EuiLoadingSpinner, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { BASE_PATH, Section, routeToAlertDetails } from './constants'; import { AppContextProvider, useAppDependencies } from './app_context'; import { hasShowAlertsCapability } from './lib/capabilities'; @@ -31,11 +30,6 @@ const AlertDetailsRoute = lazy(() => import('./sections/alert_details/components/alert_details_route') ); -const TriggersActionsUIHome = lazy(async () => import('./home')); -const AlertDetailsRoute = lazy(() => - import('./sections/alert_details/components/alert_details_route') -); - export interface AppDeps { dataPlugin: DataPublicPluginStart; charts: ChartsPluginStart; @@ -74,30 +68,15 @@ export const AppWithoutRouter = ({ sectionsRegex }: { sectionsRegex: string }) = {canShowAlerts && ( - + )} ); }; - -function suspendedRouteComponent( - RouteComponent: React.ComponentType> -) { - return (props: RouteComponentProps) => ( - - - - - - } - > - - - ); -} From e84b6be9305def64eecf5f1adfc3ada634016a77 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 11 May 2020 12:53:02 +0100 Subject: [PATCH 19/22] fixed unit tests --- .../sections/action_connector_form/action_form.test.tsx | 2 +- .../action_connector_form/connector_add_flyout.test.tsx | 2 +- .../action_connector_form/connector_edit_flyout.test.tsx | 2 +- .../components/actions_connectors_list.test.tsx | 5 +++-- .../components/actions_connectors_list.tsx | 4 +++- .../application/sections/alert_form/alert_add.test.tsx | 2 +- .../application/sections/alert_form/alert_edit.test.tsx | 2 +- 7 files changed, 11 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx index cdc187bc6f3ba6..931fde430c6018 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx @@ -10,7 +10,7 @@ import { ReactWrapper } from 'enzyme'; import { act } from 'react-dom/test-utils'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { ValidationResult, Alert, AlertAction } from '../../../types'; -import { ActionForm } from './action_form'; +import ActionForm from './action_form'; jest.mock('../../lib/action_connector_api', () => ({ loadAllActions: jest.fn(), loadActionTypes: jest.fn(), diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx index ac6c0e2749776c..4f5007949f8b14 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx @@ -6,7 +6,7 @@ import * as React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { coreMock } from '../../../../../../../src/core/public/mocks'; -import { ConnectorAddFlyout } from './connector_add_flyout'; +import ConnectorAddFlyout from './connector_add_flyout'; import { ActionsConnectorsContextProvider } from '../../context/actions_connectors_context'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { ValidationResult } from '../../../types'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.test.tsx index 976ec146181c2b..e4a9e6e74173e3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.test.tsx @@ -9,7 +9,7 @@ import { coreMock } from '../../../../../../../src/core/public/mocks'; import { ActionsConnectorsContextProvider } from '../../context/actions_connectors_context'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { ValidationResult } from '../../../types'; -import { ConnectorEditFlyout } from './connector_edit_flyout'; +import ConnectorEditFlyout from './connector_edit_flyout'; import { AppContextProvider } from '../../app_context'; const actionTypeRegistry = actionTypeRegistryMock.create(); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx index 01d21e954bbf32..12b6f993195961 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx @@ -202,11 +202,12 @@ describe('actions_connectors_list component with items', () => { expect(wrapper.find('[data-test-subj="preConfiguredTitleMessage"]')).toHaveLength(2); }); - test('if select item for edit should render ConnectorEditFlyout', () => { - wrapper + test('if select item for edit should render ConnectorEditFlyout', async () => { + await wrapper .find('[data-test-subj="edit1"]') .first() .simulate('click'); + expect(wrapper.find('ConnectorEditFlyout')).toHaveLength(1); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx index 9267a154efaa05..64a7aa9ffa8b99 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx @@ -22,7 +22,9 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { useAppDependencies } from '../../../app_context'; import { loadAllActions, loadActionTypes, deleteActions } from '../../../lib/action_connector_api'; -import { ConnectorAddFlyout, ConnectorEditFlyout } from '../../action_connector_form'; +import ConnectorAddFlyout from '../../action_connector_form/connector_add_flyout'; +import ConnectorEditFlyout from '../../action_connector_form/connector_edit_flyout'; + import { hasDeleteActionsCapability, hasSaveActionsCapability } from '../../../lib/capabilities'; import { DeleteModalConfirmation } from '../../../components/delete_modal_confirmation'; import { ActionsConnectorsContextProvider } from '../../../context/actions_connectors_context'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx index 3d6493a5131e54..bebbcdda10a003 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx @@ -9,7 +9,7 @@ import { act } from 'react-dom/test-utils'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiFormLabel } from '@elastic/eui'; import { coreMock } from '../../../../../../../src/core/public/mocks'; -import { AlertAdd } from './alert_add'; +import AlertAdd from './alert_add'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { ValidationResult } from '../../../types'; import { AlertsContextProvider, useAlertsContext } from '../../context/alerts_context'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx index 4d8801d8b7484d..39112a15095800 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx @@ -12,7 +12,7 @@ import { ValidationResult } from '../../../types'; import { AlertsContextProvider } from '../../context/alerts_context'; import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; import { ReactWrapper } from 'enzyme'; -import { AlertEdit } from './alert_edit'; +import AlertEdit from './alert_edit'; import { AppContextProvider } from '../../app_context'; const actionTypeRegistry = actionTypeRegistryMock.create(); const alertTypeRegistry = alertTypeRegistryMock.create(); From 488ee793b02968dd57a5e7fc21a4ef4b325fb40d Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 11 May 2020 13:48:14 +0100 Subject: [PATCH 20/22] support lazy loading on AlertType --- .../threshold/expression.tsx | 25 +++++----------- .../builtin_alert_types/threshold/index.ts | 9 ++++-- .../sections/alert_form/alert_form.tsx | 29 +++++++++++++------ .../common/expression_items/for_the_last.tsx | 3 +- .../common/expression_items/group_by_over.tsx | 3 +- .../triggers_actions_ui/public/index.ts | 8 ++++- .../triggers_actions_ui/public/types.ts | 22 ++++++++++++-- .../public/lib/alert_types/monitor_status.tsx | 4 ++- 8 files changed, 67 insertions(+), 36 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/expression.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/expression.tsx index 43955db97f2957..7803ed1ac3a7f6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/expression.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/expression.tsx @@ -42,6 +42,7 @@ import { } from '../../../../common'; import { builtInAggregationTypes } from '../../../../common/constants'; import { IndexThresholdAlertParams } from './types'; +import { AlertTypeParamsExpressionProps } from '../../../../types'; import { AlertsContextValue } from '../../../context/alerts_context'; import './expression.scss'; @@ -66,23 +67,10 @@ const expressionFieldsWithValidation = [ 'timeWindowSize', ]; -interface IndexThresholdProps { - alertParams: IndexThresholdAlertParams; - alertInterval: string; - setAlertParams: (property: string, value: any) => void; - setAlertProperty: (key: string, value: any) => void; - errors: { [key: string]: string[] }; - alertsContext: AlertsContextValue; -} - -export const IndexThresholdAlertTypeExpression: React.FunctionComponent = ({ - alertParams, - alertInterval, - setAlertParams, - setAlertProperty, - errors, - alertsContext, -}) => { +export const IndexThresholdAlertTypeExpression: React.FunctionComponent> = ({ alertParams, alertInterval, setAlertParams, setAlertProperty, errors, alertsContext }) => { const { index, timeField, @@ -476,3 +464,6 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent ); }; + +// eslint-disable-next-line import/no-default-export +export { IndexThresholdAlertTypeExpression as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/index.ts index 983f759214b6b7..42747b9e85e2e5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/index.ts @@ -3,16 +3,19 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { lazy } from 'react'; + import { AlertTypeModel } from '../../../../types'; -import { IndexThresholdAlertTypeExpression } from './expression'; import { validateExpression } from './validation'; +import { IndexThresholdAlertParams } from './types'; +import { AlertsContextValue } from '../../../context/alerts_context'; -export function getAlertType(): AlertTypeModel { +export function getAlertType(): AlertTypeModel { return { id: '.index-threshold', name: 'Index threshold', iconClass: 'alert', - alertParamsExpression: IndexThresholdAlertTypeExpression, + alertParamsExpression: lazy(() => import('./expression')), validate: validateExpression, }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx index 6d23a6fb66f353..e956c8ecc4f3bc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx @@ -3,7 +3,7 @@ * 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, useState, useEffect } from 'react'; +import React, { Fragment, useState, useEffect, Suspense } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { @@ -23,6 +23,7 @@ import { EuiIconTip, EuiButtonIcon, EuiHorizontalRule, + EuiLoadingSpinner, } from '@elastic/eui'; import { some, filter, map, fold } from 'fp-ts/lib/Option'; import { pipe } from 'fp-ts/lib/pipeable'; @@ -222,14 +223,24 @@ export const AlertForm = ({ ) : null} {AlertParamsExpressionComponent ? ( - + + + + + + } + > + + ) : null} {defaultActionGroupId ? ( void; onChangeWindowUnit: (selectedWindowUnit: string) => void; popupPosition?: diff --git a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/group_by_over.tsx b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/group_by_over.tsx index 619d85d99719b3..33ca98de4c08b4 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/group_by_over.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/group_by_over.tsx @@ -19,10 +19,11 @@ import { import { builtInGroupByTypes } from '../constants'; import { GroupByType } from '../types'; import { ClosablePopoverTitle } from './components'; +import { IErrorObject } from '../../types'; interface GroupByExpressionProps { groupBy: string; - errors: { [key: string]: string[] }; + errors: IErrorObject; onChangeSelectedTermSize: (selectedTermSize?: number) => void; onChangeSelectedTermField: (selectedTermField?: string) => void; onChangeSelectedGroupBy: (selectedGroupBy?: string) => void; diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index 96645e856e4183..a72d8815c95b4d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -11,7 +11,13 @@ export { AlertsContextProvider } from './application/context/alerts_context'; export { ActionsConnectorsContextProvider } from './application/context/actions_connectors_context'; export { AlertAdd } from './application/sections/alert_form'; export { ActionForm } from './application/sections/action_connector_form'; -export { AlertAction, Alert, AlertTypeModel, ActionType } from './types'; +export { + AlertAction, + Alert, + AlertTypeModel, + AlertTypeParamsExpressionProps, + ActionType, +} from './types'; export { ConnectorAddFlyout, ConnectorEditFlyout, diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index cc511434267cc2..5dd493ee25990c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -110,12 +110,28 @@ export interface AlertTableItem extends Alert { tagsText: string; } -export interface AlertTypeModel { +export interface AlertTypeParamsExpressionProps< + AlertParamsType = unknown, + AlertsContextValue = unknown +> { + alertParams: AlertParamsType; + alertInterval: string; + setAlertParams: (property: string, value: any) => void; + setAlertProperty: (key: string, value: any) => void; + errors: IErrorObject; + alertsContext: AlertsContextValue; +} + +export interface AlertTypeModel { id: string; name: string | JSX.Element; iconClass: string; - validate: (alertParams: any) => ValidationResult; - alertParamsExpression: React.FunctionComponent; + validate: (alertParams: AlertParamsType) => ValidationResult; + alertParamsExpression: + | React.FunctionComponent> + | React.LazyExoticComponent< + ComponentType> + >; defaultActionMessage?: string; } diff --git a/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx b/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx index 66e61fbf73b649..65827867da5ee8 100644 --- a/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx +++ b/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx @@ -63,7 +63,9 @@ export const initMonitorStatusAlertType: AlertTypeInitializer = ({ id: CLIENT_ALERT_TYPES.MONITOR_STATUS, name: , iconClass: 'uptimeApp', - alertParamsExpression: params => , + alertParamsExpression: (params: any) => ( + + ), validate, defaultActionMessage, }); From 041e1777a920e39d48074f97f845c9e341c914cc Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 11 May 2020 13:53:12 +0100 Subject: [PATCH 21/22] change type back to any as usage is wrong in other plugins --- x-pack/plugins/triggers_actions_ui/public/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 5dd493ee25990c..e9cfd5b33db233 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -128,7 +128,7 @@ export interface AlertTypeModel iconClass: string; validate: (alertParams: AlertParamsType) => ValidationResult; alertParamsExpression: - | React.FunctionComponent> + | React.FunctionComponent | React.LazyExoticComponent< ComponentType> >; From 0736325350b54dca0b28d055c7fca68f497b7705 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Tue, 12 May 2020 10:35:51 +0100 Subject: [PATCH 22/22] updated docs --- x-pack/plugins/triggers_actions_ui/README.md | 23 ++++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/README.md b/x-pack/plugins/triggers_actions_ui/README.md index c5f02863ba8a19..0ed6917854dc40 100644 --- a/x-pack/plugins/triggers_actions_ui/README.md +++ b/x-pack/plugins/triggers_actions_ui/README.md @@ -69,13 +69,13 @@ export function getAlertType(): AlertTypeModel { id: '.index-threshold', name: 'Index threshold', iconClass: 'alert', - alertParamsExpression: IndexThresholdAlertTypeExpression, + alertParamsExpression: lazy(() => import('./index_threshold_expression')), validate: validateAlertType, }; } ``` -alertParamsExpression form represented as an expression using `EuiExpression` components: +alertParamsExpression should be a lazy loaded React component extending an expression using `EuiExpression` components: ![Index Threshold Alert expression form](https://i.imgur.com/Ysk1ljY.png) ``` @@ -171,6 +171,7 @@ export const alertReducer = (state: any, action: AlertReducerAction) => { ``` +The Expression component should be lazy loaded which means it'll have to be the default export in `index_threshold_expression.ts`: ``` export const IndexThresholdAlertTypeExpression: React.FunctionComponent = ({ @@ -224,6 +225,9 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent ); }; + +// Export as default in order to support lazy loading +export {IndexThresholdAlertTypeExpression as default}; ``` Index Threshold Alert form with validation: @@ -237,7 +241,9 @@ Each alert type should be defined as `AlertTypeModel` object with the these prop name: string; iconClass: string; validate: (alertParams: any) => ValidationResult; - alertParamsExpression: React.FunctionComponent; + alertParamsExpression: React.LazyExoticComponent< + ComponentType> + >; defaultActionMessage?: string; ``` |Property|Description| @@ -246,7 +252,7 @@ Each alert type should be defined as `AlertTypeModel` object with the these prop |name|Name of the alert type that will be displayed on the select card in the UI.| |iconClass|Icon of the alert type that will be displayed on the select card in the UI.| |validate|Validation function for the alert params.| -|alertParamsExpression|React functional component for building UI of the current alert type params.| +|alertParamsExpression| A lazy loaded React component for building UI of the current alert type params.| |defaultActionMessage|Optional property for providing default message for all added actions with `message` property.| IMPORTANT: The current UI supports a single action group only. @@ -295,8 +301,8 @@ Below is a list of steps that should be done to build and register a new alert t 1. At any suitable place in Kibana, create a file, which will expose an object implementing interface [AlertTypeModel](https://github.com/elastic/kibana/blob/55b7905fb5265b73806006e7265739545d7521d0/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/types.ts#L83). Example: ``` +import { lazy } from 'react'; import { AlertTypeModel } from '../../../../types'; -import { ExampleExpression } from './expression'; import { validateExampleAlertType } from './validation'; export function getAlertType(): AlertTypeModel { @@ -304,7 +310,7 @@ export function getAlertType(): AlertTypeModel { id: 'example', name: 'Example Alert Type', iconClass: 'bell', - alertParamsExpression: ExampleExpression, + alertParamsExpression: lazy(() => import('./expression')), validate: validateExampleAlertType, defaultActionMessage: 'Alert [{{ctx.metadata.name}}] has exceeded the threshold', }; @@ -361,6 +367,9 @@ export const ExampleExpression: React.FunctionComponent = ({ ); }; +// Export as default in order to support lazy loading +export {ExampleExpression as default}; + ``` This alert type form becomes available, when the card of `Example Alert Type` is selected. Each expression word here is `EuiExpression` component and implements the basic aggregation, grouping and comparison methods. @@ -1017,7 +1026,7 @@ Below is a list of steps that should be done to build and register a new action 1. At any suitable place in Kibana, create a file, which will expose an object implementing interface [ActionTypeModel]: ``` -import React, { Fragment } from 'react'; +import React, { Fragment, lazy } from 'react'; import { i18n } from '@kbn/i18n'; import { ActionTypeModel,