From 2c387e9161209b0809f00c6b1453e3fbd557fbc4 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Fri, 15 Oct 2021 18:28:19 -0400 Subject: [PATCH 01/18] adding deprecated icon to the other actions list --- .../action_type_form.test.tsx | 223 ++++++++++++------ .../action_type_form.tsx | 181 +++++++------- .../connector_add_inline.test.tsx | 192 +++++++++++++++ .../connector_add_inline.tsx | 66 ++---- .../connector_dropdown.tsx | 102 ++++++++ .../deprecated_connector.tsx | 55 +++++ .../application/sections/common/connectors.ts | 51 ++++ 7 files changed, 660 insertions(+), 210 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/deprecated_connector.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx index e8590595b9d61..4bd9032cbc6f5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx @@ -6,6 +6,7 @@ */ import * as React from 'react'; import { mountWithIntl, nextTick } from '@kbn/test/jest'; + import { ActionTypeForm } from './action_type_form'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { @@ -17,6 +18,7 @@ import { } from '../../../types'; import { act } from 'react-dom/test-utils'; import { EuiFieldText } from '@elastic/eui'; +import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common'; import { DefaultActionParams } from '../../lib/get_defaults_for_action_params'; jest.mock('../../../common/lib/kibana'); @@ -38,7 +40,7 @@ describe('action_type_form', () => { }, })); - it('calls "setActionParamsProperty" to set the default value for the empty dedupKey', async () => { + beforeEach(() => { const actionType = actionTypeRegistryMock.createMockActionTypeModel({ id: '.pagerduty', iconClass: 'test', @@ -54,22 +56,101 @@ describe('action_type_form', () => { actionParamsFields: mockedActionParamsFields, }); actionTypeRegistry.get.mockReturnValue(actionType); + }); + + describe('deprecated icon', () => { + const notDeprecatedConnector = { + actionTypeId: '.pagerduty', + config: { + apiUrl: 'http:\\test', + }, + id: 'test', + isPreconfigured: false, + name: 'test name', + secrets: {}, + }; + const actionItem = { + id: 'test', + actionTypeId: '.pagerduty', + group: 'recovered', + params: { + eventAction: 'recovered', + dedupKey: undefined, + summary: '2323', + source: 'source', + severity: '1', + timestamp: new Date().toISOString(), + component: 'test', + group: 'group', + class: 'test class', + }, + }; + + it('renders the deprecated warning icon for deprecated connectors', async () => { + const deprecatedConnector = { + ...notDeprecatedConnector, + config: { ...notDeprecatedConnector.config, isLegacy: true }, + }; + + const wrapper = mountWithIntl( + getActionTypeForm({ + actionConnector: deprecatedConnector, + connectors: [deprecatedConnector], + actionItem, + }) + ); + + // Wait for active space to resolve before requesting the component to update + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect( + wrapper.find('[data-test-subj="deprecated-connector-icon-test"]').exists() + ).toBeTruthy(); + }); + + it('does not render the deprecated warning icon for non-deprecated connectors', async () => { + const wrapper = mountWithIntl( + getActionTypeForm({ + actionConnector: notDeprecatedConnector, + connectors: [notDeprecatedConnector], + actionItem, + }) + ); + + // Wait for active space to resolve before requesting the component to update + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect( + wrapper.find('[data-test-subj="deprecated-connector-icon-test"]').exists() + ).toBeFalsy(); + }); + }); + + it('calls "setActionParamsProperty" to set the default value for the empty dedupKey', async () => { const wrapper = mountWithIntl( - getActionTypeForm(1, undefined, { - id: '123', - actionTypeId: '.pagerduty', - group: 'recovered', - params: { - eventAction: 'recovered', - dedupKey: undefined, - summary: '2323', - source: 'source', - severity: '1', - timestamp: new Date().toISOString(), - component: 'test', - group: 'group', - class: 'test class', + getActionTypeForm({ + actionItem: { + id: '123', + actionTypeId: '.pagerduty', + group: 'recovered', + params: { + eventAction: 'recovered', + dedupKey: undefined, + summary: '2323', + source: 'source', + severity: '1', + timestamp: new Date().toISOString(), + component: 'test', + group: 'group', + class: 'test class', + }, }, }) ); @@ -88,37 +169,24 @@ describe('action_type_form', () => { }); it('does not call "setActionParamsProperty" because dedupKey is not empty', async () => { - const actionType = actionTypeRegistryMock.createMockActionTypeModel({ - id: '.pagerduty', - iconClass: 'test', - selectMessage: 'test', - validateConnector: (): Promise> => { - return Promise.resolve({}); - }, - validateParams: (): Promise> => { - const validationResult = { errors: {} }; - return Promise.resolve(validationResult); - }, - actionConnectorFields: null, - actionParamsFields: mockedActionParamsFields, - }); - actionTypeRegistry.get.mockReturnValue(actionType); - const wrapper = mountWithIntl( - getActionTypeForm(1, undefined, { - id: '123', - actionTypeId: '.pagerduty', - group: 'recovered', - params: { - eventAction: 'recovered', - dedupKey: '232323', - summary: '2323', - source: 'source', - severity: '1', - timestamp: new Date().toISOString(), - component: 'test', - group: 'group', - class: 'test class', + getActionTypeForm({ + index: 1, + actionItem: { + id: '123', + actionTypeId: '.pagerduty', + group: 'recovered', + params: { + eventAction: 'recovered', + dedupKey: '232323', + summary: '2323', + source: 'source', + severity: '1', + timestamp: new Date().toISOString(), + component: 'test', + group: 'group', + class: 'test class', + }, }, }) ); @@ -135,18 +203,29 @@ describe('action_type_form', () => { }); }); -function getActionTypeForm( - index?: number, - actionConnector?: ActionConnector, Record>, - actionItem?: AlertAction, - defaultActionGroupId?: string, - connectors?: Array, Record>>, - actionTypeIndex?: Record, - defaultParams?: DefaultActionParams, - onAddConnector?: () => void, - onDeleteAction?: () => void, - onConnectorSelected?: (id: string) => void -) { +function getActionTypeForm({ + index, + actionConnector, + actionItem, + defaultActionGroupId, + connectors, + actionTypeIndex, + defaultParams, + onAddConnector, + onDeleteAction, + onConnectorSelected, +}: { + index?: number; + actionConnector?: ActionConnector, Record>; + actionItem?: AlertAction; + defaultActionGroupId?: string; + connectors?: Array, Record>>; + actionTypeIndex?: Record; + defaultParams?: DefaultActionParams; + onAddConnector?: () => void; + onDeleteAction?: () => void; + onConnectorSelected?: (id: string) => void; +}) { const actionConnectorDefault = { actionTypeId: '.pagerduty', config: { @@ -213,19 +292,21 @@ function getActionTypeForm( eventAction: 'resolve', }; return ( - + + + ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx index 526d899b7efb1..25a7941540d32 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx @@ -14,7 +14,6 @@ import { EuiIcon, EuiSpacer, EuiFormRow, - EuiComboBox, EuiAccordion, EuiButtonIcon, EuiButtonEmpty, @@ -36,13 +35,20 @@ import { ActionVariables, ActionTypeRegistryContract, REQUIRED_ACTION_VARIABLES, + ActionTypeModel, } from '../../../types'; -import { checkActionFormActionTypeEnabled } from '../../lib/check_action_type_enabled'; +import { + checkActionFormActionTypeEnabled, + IsDisabledResult, + IsEnabledResult, +} from '../../lib/check_action_type_enabled'; import { hasSaveActionsCapability } from '../../lib/capabilities'; import { ActionAccordionFormProps, ActionGroupWithMessageVariables } from './action_form'; import { transformActionVariables } from '../../lib/action_variables'; import { useKibana } from '../../../common/lib/kibana'; import { DefaultActionParams } from '../../lib/get_defaults_for_action_params'; +import { ConnectorsDropdown } from './connector_dropdown'; +import { DeprecatedConnectorIcon } from './deprecated_connector'; export type ActionTypeFormProps = { actionItem: AlertAction; @@ -140,32 +146,6 @@ export const ActionTypeForm = ({ }, [actionItem]); const canSave = hasSaveActionsCapability(capabilities); - const getSelectedOptions = (actionItemId: string) => { - const selectedConnector = connectors.find((connector) => connector.id === actionItemId); - if ( - !selectedConnector || - // if selected connector is not preconfigured and action type is for preconfiguration only, - // do not show regular connectors of this type - (actionTypesIndex && - !actionTypesIndex[selectedConnector.actionTypeId].enabledInConfig && - !selectedConnector.isPreconfigured) - ) { - return []; - } - const optionTitle = `${selectedConnector.name} ${ - selectedConnector.isPreconfigured ? preconfiguredMessage : '' - }`; - return [ - { - label: optionTitle, - value: optionTitle, - id: actionItemId, - 'data-test-subj': 'itemActionConnector', - }, - ]; - }; - - const actionType = actionTypesIndex[actionItem.actionTypeId]; const actionGroupDisplay = ( actionGroupId: string, @@ -189,18 +169,6 @@ export const ActionTypeForm = ({ ? isActionGroupDisabledForActionType(actionGroupId, actionTypeId) : false; - const optionsList = connectors - .filter( - (connectorItem) => - connectorItem.actionTypeId === actionItem.actionTypeId && - // include only enabled by config connectors or preconfigured - (actionType.enabledInConfig || connectorItem.isPreconfigured) - ) - .map(({ name, id, isPreconfigured }) => ({ - label: `${name} ${isPreconfigured ? preconfiguredMessage : ''}`, - key: id, - id, - })); const actionTypeRegistered = actionTypeRegistry.get(actionConnector.actionTypeId); if (!actionTypeRegistered) return null; @@ -283,17 +251,12 @@ export const ActionTypeForm = ({ ) : null } > - { - onConnectorSelected(selectedOptions[0].id ?? ''); - }} - isClearable={false} + @@ -330,52 +293,12 @@ export const ActionTypeForm = ({ buttonContentClassName="actAccordionActionForm__button" data-test-subj={`alertActionAccordion-${index}`} buttonContent={ - - - - - - -
- - - - - {selectedActionGroup && !isOpen && ( - - {selectedActionGroup.name} - - )} - - {checkEnabledResult.isEnabled === false && ( - <> - - - )} - - -
-
-
-
+ } extraAction={ { + return ( + + + + + + +
+ + + + + {selectedActionGroup && !isOpen && ( + + {selectedActionGroup.name} + + )} + + {checkEnabledResult.isEnabled === false && ( + <> + + + )} + + + +
+
+
+
+ ); +}; + function getAvailableActionVariables( actionVariables: ActionVariables, actionGroup?: ActionGroupWithMessageVariables diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.test.tsx new file mode 100644 index 0000000000000..ee2735fc95e87 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.test.tsx @@ -0,0 +1,192 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { mountWithIntl, nextTick } from '@kbn/test/jest'; + +import { actionTypeRegistryMock } from '../../action_type_registry.mock'; +import { + ActionConnector, + ActionType, + AlertAction, + ConnectorValidationResult, + GenericValidationResult, +} from '../../../types'; +import { act } from 'react-dom/test-utils'; +import { EuiFieldText } from '@elastic/eui'; +import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common'; +import AddConnectorInline from './connector_add_inline'; +import { useKibana } from '../../../common/lib/kibana'; +import { coreMock } from '../../../../../../../src/core/public/mocks'; + +jest.mock('../../../common/lib/kibana'); +const useKibanaMock = useKibana as jest.Mocked; +const actionTypeRegistry = actionTypeRegistryMock.create(); +const mocks = coreMock.createSetup(); + +describe('connector_add_inline', () => { + const mockedActionParamsFields = React.lazy(async () => ({ + default() { + return ( + <> + true} + fullWidth + /> + + ); + }, + })); + + beforeEach(async () => { + const [ + { + application: { capabilities }, + }, + ] = await mocks.getStartServices(); + useKibanaMock().services.application.capabilities = { + ...capabilities, + actions: { + delete: true, + save: true, + show: true, + }, + }; + const actionType = actionTypeRegistryMock.createMockActionTypeModel({ + id: '.pagerduty', + iconClass: 'test', + selectMessage: 'test', + validateConnector: (): Promise> => { + return Promise.resolve({}); + }, + validateParams: (): Promise> => { + const validationResult = { errors: {} }; + return Promise.resolve(validationResult); + }, + actionConnectorFields: null, + actionParamsFields: mockedActionParamsFields, + actionTypeTitle: 'pagerduty', + }); + actionTypeRegistry.get.mockReturnValue(actionType); + }); + + describe('deprecated icon', () => { + const notDeprecatedConnector = { + actionTypeId: '.pagerduty', + config: { + apiUrl: 'http:\\test', + }, + id: '123', + isPreconfigured: true, + name: 'test name', + secrets: {}, + }; + + const actionItem = { + id: '123', + actionTypeId: '.pagerduty', + group: 'recovered', + params: { + eventAction: 'recovered', + dedupKey: undefined, + summary: '2323', + source: 'source', + severity: '1', + timestamp: new Date().toISOString(), + component: 'test', + group: 'group', + class: 'test class', + }, + }; + + it('renders the deprecated warning icon for deprecated connectors', async () => { + const deprecatedConnector = { + ...notDeprecatedConnector, + config: { ...notDeprecatedConnector.config, isLegacy: true }, + }; + + const wrapper = mountWithIntl( + createConnectorAddInlineComponent({ + connectors: [deprecatedConnector], + actionItem, + }) + ); + + // Wait for active space to resolve before requesting the component to update + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect( + wrapper.find('[data-test-subj="deprecated-connector-icon-123"]').exists() + ).toBeTruthy(); + }); + + it('does not render the deprecated warning icon for non-deprecated connectors', async () => { + const wrapper = mountWithIntl( + createConnectorAddInlineComponent({ + connectors: [notDeprecatedConnector], + actionItem, + }) + ); + + // Wait for active space to resolve before requesting the component to update + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect(wrapper.find('[data-test-subj="deprecated-connector-icon-123"]').exists()).toBeFalsy(); + }); + }); +}); + +function createConnectorAddInlineComponent({ + actionItem, + connectors, +}: { + actionItem: AlertAction; + connectors: ActionConnector[]; +}) { + const actionTypeIndex: Record = { + '.pagerduty': { + id: '.pagerduty', + enabled: true, + name: 'Test', + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'basic', + }, + '.server-log': { + id: '.server-log', + enabled: true, + name: 'Test SL', + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'basic', + }, + }; + + return ( + + + + ); +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx index 0cdcf8bd44413..1d847b764902b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { @@ -21,14 +21,14 @@ import { EuiText, EuiFormRow, EuiButtonEmpty, - EuiComboBox, - EuiComboBoxOptionOption, EuiIconTip, } from '@elastic/eui'; import { AlertAction, ActionTypeIndex, ActionConnector } from '../../../types'; import { hasSaveActionsCapability } from '../../lib/capabilities'; import { ActionAccordionFormProps } from './action_form'; import { useKibana } from '../../../common/lib/kibana'; +import { getEnabledAndConfiguredConnectors } from '../common/connectors'; +import { ConnectorsDropdown } from './connector_dropdown'; type AddConnectorInFormProps = { actionTypesIndex: ActionTypeIndex; @@ -56,15 +56,17 @@ export const AddConnectorInline = ({ application: { capabilities }, } = useKibana().services; const canSave = hasSaveActionsCapability(capabilities); - const [connectorOptionsList, setConnectorOptionsList] = useState([]); + const [hasConnectors, setHasConnectors] = useState(false); const [isEmptyActionId, setIsEmptyActionId] = useState(false); - const [errors, setErrors] = useState([]); const actionTypeName = actionTypesIndex ? actionTypesIndex[actionItem.actionTypeId].name : actionItem.actionTypeId; - const actionType = actionTypesIndex[actionItem.actionTypeId]; const actionTypeRegistered = actionTypeRegistry.get(actionItem.actionTypeId); + const connectorDropdownErrors = useMemo( + () => [`Unable to load ${actionTypeRegistered.actionTypeTitle} connector`], + [actionTypeRegistered.actionTypeTitle] + ); const noConnectorsLabel = ( { - if (connectors) { - const altConnectorOptions = connectors - .filter( - (connector) => - connector.actionTypeId === actionItem.actionTypeId && - // include only enabled by config connectors or preconfigured - (actionType?.enabledInConfig || connector.isPreconfigured) - ) - .map(({ name, id, isPreconfigured }) => ({ - label: `${name} ${isPreconfigured ? '(preconfigured)' : ''}`, - key: id, - id, - })); - setConnectorOptionsList(altConnectorOptions); + const filteredConnectors = getEnabledAndConfiguredConnectors( + connectors, + actionItem, + actionTypesIndex + ); - if (altConnectorOptions.length > 0) { - setErrors([`Unable to load ${actionTypeRegistered.actionTypeTitle} connector`]); - } + if (filteredConnectors.length > 0) { + setHasConnectors(true); } setIsEmptyActionId(!!emptyActionsIds.find((emptyId: string) => actionItem.id === emptyId)); @@ -136,25 +128,15 @@ export const AddConnectorInline = ({ /> } - error={errors} - isInvalid={errors.length > 0} + error={connectorDropdownErrors} + isInvalid > - { - // On selecting a option from this combo box, this component will - // be removed but the EuiComboBox performs some additional updates on - // closing the dropdown. Wrapping in a `setTimeout` to avoid `React state - // update on an unmounted component` warnings. - setTimeout(() => { - onSelectConnector(selectedOptions[0].id ?? ''); - }); - }} - isClearable={false} + @@ -223,7 +205,7 @@ export const AddConnectorInline = ({ paddingSize="l" > {canSave ? ( - connectorOptionsList.length > 0 ? ( + hasConnectors ? ( connectorsDropdown ) : ( void; +} + +export const ConnectorsDropdown = React.memo(ConnectorsDropdownComponent); + +function ConnectorsDropdownComponent({ + actionItem, + accordionIndex, + actionTypesIndex, + connectors, + onConnectorSelected, +}: DropdownProps) { + const validConnectors = useMemo( + () => getEnabledAndConfiguredConnectors(connectors, actionItem, actionTypesIndex), + [actionItem, actionTypesIndex, connectors] + ); + + const valueOfSelected = useMemo( + () => getValueOfSelectedConnector(actionItem.id, validConnectors), + [actionItem.id, validConnectors] + ); + + const options = useMemo(() => createConnectorOptions(validConnectors), [validConnectors]); + + const onChange = useCallback((id: string) => onConnectorSelected(id), [onConnectorSelected]); + + return ( + + ); +} + +const getValueOfSelectedConnector = ( + actionItemId: string, + connectors: ActionConnector[] +): string | undefined => { + const selectedConnector = connectors.find((connector) => connector.id === actionItemId); + + if (!selectedConnector) { + return; + } + + return actionItemId; +}; + +const createConnectorOptions = (connectors: ActionConnector[]) => + connectors.map((connector) => { + const title = `${connector.name} ${connector.isPreconfigured ? preconfiguredMessage : ''}`; + + return { + value: connector.id, + inputDisplay: ( + + + {title} + + + + ), + 'data-test-subj': `dropdown-connector-${connector.id}`, + }; + }); + +const preconfiguredMessage = i18n.translate( + 'xpack.triggersActionsUI.sections.actionForm.preconfiguredTitleMessage', + { + defaultMessage: '(preconfigured)', + } +); + +const incidentManagemSystem = i18n.translate( + 'xpack.triggersActionsUI.sections.actionForm.incidentManagementSystemLabel', + { + defaultMessage: 'Incident management system', + } +); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/deprecated_connector.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/deprecated_connector.tsx new file mode 100644 index 0000000000000..cccc3892a4075 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/deprecated_connector.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiFlexItem, EuiIconTip } from '@elastic/eui'; +import React from 'react'; +import { i18n } from '@kbn/i18n'; + +import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; +import { ActionConnector } from '../../../types'; +import { isDeprecatedConnector } from '../common/connectors'; + +export const DeprecatedConnectorIcon = React.memo(DeprecatedConnectorComponent); + +function DeprecatedConnectorComponent({ connector }: { connector: ActionConnector }) { + return ( + <> + {isDeprecatedConnector(connector) && ( + + + + )} + + ); +} + +const StyledIconTip = euiStyled(EuiIconTip)` + margin-left: ${({ theme }) => theme.eui.euiSizeS} + margin-bottom: 0 !important; +`; + +const deprecatedTooltipTitle = i18n.translate( + 'xpack.triggersActionsUI.sections.actionForm.deprecatedTooltipTitle', + { + defaultMessage: 'Deprecated connector', + } +); + +const deprecatedTooltipContent = i18n.translate( + 'xpack.triggersActionsUI.sections.actionForm.deprecatedTooltipContent', + { + defaultMessage: 'Please update your connector', + } +); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts new file mode 100644 index 0000000000000..ef60000619f97 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + ENABLE_NEW_SN_ITSM_CONNECTOR, + ENABLE_NEW_SN_SIR_CONNECTOR, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../../actions/server/constants/connectors'; +import { + ActionConnector, + ActionTypeIndex, + AlertAction, + UserConfiguredActionConnector, +} from '../../../types'; + +export const isDeprecatedConnector = (connector: ActionConnector): boolean => { + // TODO: Remove ENABLE_* portion when the applications are certified + if (!ENABLE_NEW_SN_ITSM_CONNECTOR && connector.actionTypeId === '.servicenow') { + return true; + } + + if (!ENABLE_NEW_SN_SIR_CONNECTOR && connector.actionTypeId === '.servicenow-sir') { + return true; + } + + // TODO: add a type guard + const unsafeConfig = ( + connector as UserConfiguredActionConnector, Record> + ).config; + + return !!unsafeConfig.isLegacy; +}; + +export const getEnabledAndConfiguredConnectors = ( + connectors: ActionConnector[], + actionItem: AlertAction, + actionTypesIndex: ActionTypeIndex +): ActionConnector[] => { + const actionType = actionTypesIndex[actionItem.actionTypeId]; + + return connectors.filter( + (connector) => + connector.actionTypeId === actionItem.actionTypeId && + // include only enabled by config connectors or preconfigured + (actionType?.enabledInConfig || connector.isPreconfigured) + ); +}; From 6c93aa59efa438dcd758defcfad315791cebb950 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Wed, 20 Oct 2021 11:56:27 -0400 Subject: [PATCH 02/18] Adding deprecated text to list view --- .../action_connector_form/action_form.tsx | 3 ++- .../action_type_form.tsx | 2 -- .../connector_add_inline.tsx | 8 ++----- .../connector_dropdown.tsx | 20 +++++++++++++--- .../components/actions_connectors_list.tsx | 24 +++++++------------ .../application/sections/common/connectors.ts | 13 ++++++++-- 6 files changed, 41 insertions(+), 29 deletions(-) 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 eda0b99e859a6..3dbcc2a66dd23 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 @@ -40,6 +40,7 @@ import { useKibana } from '../../../common/lib/kibana'; import { DefaultActionParamsGetter } from '../../lib/get_defaults_for_action_params'; import { ConnectorAddModal } from '.'; import { suspendedComponentWithProps } from '../../lib/suspended_component_with_props'; +import { isDeprecatedConnector } from '../common/connectors'; export interface ActionGroupWithMessageVariables extends ActionGroup { omitOptionalMessageVariables?: boolean; @@ -309,7 +310,7 @@ export const ActionForm = ({ actions.map((actionItem: AlertAction, index: number) => { const actionConnector = connectors.find((field) => field.id === actionItem.id); // connectors doesn't exists - if (!actionConnector) { + if (!actionConnector || isDeprecatedConnector(actionConnector)) { return ( )} - diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx index 1d847b764902b..956e6e98bbb5f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx @@ -27,7 +27,7 @@ import { AlertAction, ActionTypeIndex, ActionConnector } from '../../../types'; import { hasSaveActionsCapability } from '../../lib/capabilities'; import { ActionAccordionFormProps } from './action_form'; import { useKibana } from '../../../common/lib/kibana'; -import { getEnabledAndConfiguredConnectors } from '../common/connectors'; +import { getValidConnectors } from '../common/connectors'; import { ConnectorsDropdown } from './connector_dropdown'; type AddConnectorInFormProps = { @@ -88,11 +88,7 @@ export const AddConnectorInline = ({ ); useEffect(() => { - const filteredConnectors = getEnabledAndConfiguredConnectors( - connectors, - actionItem, - actionTypesIndex - ); + const filteredConnectors = getValidConnectors(connectors, actionItem, actionTypesIndex); if (filteredConnectors.length > 0) { setHasConnectors(true); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx index 264f62084358b..6c14a540e20d4 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx @@ -10,7 +10,7 @@ import React, { useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { ActionConnector, ActionTypeIndex, AlertAction } from '../../../types'; -import { getEnabledAndConfiguredConnectors } from '../common/connectors'; +import { deprecatedMessage, getValidConnectors, isDeprecatedConnector } from '../common/connectors'; import { DeprecatedConnectorIcon } from './deprecated_connector'; export interface DropdownProps { @@ -31,7 +31,7 @@ function ConnectorsDropdownComponent({ onConnectorSelected, }: DropdownProps) { const validConnectors = useMemo( - () => getEnabledAndConfiguredConnectors(connectors, actionItem, actionTypesIndex), + () => getValidConnectors(connectors, actionItem, actionTypesIndex), [actionItem, actionTypesIndex, connectors] ); @@ -71,7 +71,7 @@ const getValueOfSelectedConnector = ( const createConnectorOptions = (connectors: ActionConnector[]) => connectors.map((connector) => { - const title = `${connector.name} ${connector.isPreconfigured ? preconfiguredMessage : ''}`; + const title = getTitle(connector); return { value: connector.id, @@ -87,6 +87,20 @@ const createConnectorOptions = (connectors: ActionConnector[]) => }; }); +const getTitle = (connector: ActionConnector) => { + let title = connector.name; + + if (connector.isPreconfigured) { + title += ` ${preconfiguredMessage}`; + } + + if (isDeprecatedConnector(connector)) { + title += ` ${deprecatedMessage}`; + } + + return title; +}; + const preconfiguredMessage = i18n.translate( 'xpack.triggersActionsUI.sections.actionForm.preconfiguredTitleMessage', { 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 844f28f022547..8a8fefa7ae85c 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 @@ -40,7 +40,6 @@ import { ActionConnectorTableItem, ActionTypeIndex, EditConectorTabs, - UserConfiguredActionConnector, } from '../../../../types'; import { EmptyConnectorsPrompt } from '../../../components/prompts/empty_connectors_prompt'; import { useKibana } from '../../../../common/lib/kibana'; @@ -48,11 +47,7 @@ import { DEFAULT_HIDDEN_ACTION_TYPES } from '../../../../'; import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner'; import ConnectorEditFlyout from '../../action_connector_form/connector_edit_flyout'; import ConnectorAddFlyout from '../../action_connector_form/connector_add_flyout'; -import { - ENABLE_NEW_SN_ITSM_CONNECTOR, - ENABLE_NEW_SN_SIR_CONNECTOR, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../../actions/server/constants/connectors'; +import { deprecatedMessage, isDeprecatedConnector } from '../../common/connectors'; const ConnectorIconTipWithSpacing = withTheme(({ theme }: { theme: EuiTheme }) => { return ( @@ -202,14 +197,9 @@ const ActionsConnectorsList: React.FunctionComponent = () => { const checkEnabledResult = checkActionTypeEnabled( actionTypesIndex && actionTypesIndex[item.actionTypeId] ); - const itemConfig = ( - item as UserConfiguredActionConnector, Record> - ).config; - const showLegacyTooltip = - itemConfig?.isLegacy && - // TODO: Remove when applications are certified - ((ENABLE_NEW_SN_ITSM_CONNECTOR && item.actionTypeId === '.servicenow') || - (ENABLE_NEW_SN_SIR_CONNECTOR && item.actionTypeId === '.servicenow-sir')); + + const showLegacyTooltip = isDeprecatedConnector(item); + const name = getConnectorName(value, item); const link = ( <> @@ -219,7 +209,7 @@ const ActionsConnectorsList: React.FunctionComponent = () => { key={item.id} disabled={actionTypesIndex ? !actionTypesIndex[item.actionTypeId]?.enabled : true} > - {value} + {name} {item.isMissingSecrets ? ( action.actionTypeId === actionTypeId).length; } +function getConnectorName(name: string, connector: ActionConnector): string { + return isDeprecatedConnector(connector) ? `${name} ${deprecatedMessage}` : name; +} + const DeleteOperation: React.FunctionComponent<{ item: ActionConnectorTableItem; canDelete: boolean; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts index ef60000619f97..378fdf206ecdd 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { i18n } from '@kbn/i18n'; + import { ENABLE_NEW_SN_ITSM_CONNECTOR, ENABLE_NEW_SN_SIR_CONNECTOR, @@ -32,10 +34,10 @@ export const isDeprecatedConnector = (connector: ActionConnector): boolean => { connector as UserConfiguredActionConnector, Record> ).config; - return !!unsafeConfig.isLegacy; + return !!unsafeConfig?.isLegacy; }; -export const getEnabledAndConfiguredConnectors = ( +export const getValidConnectors = ( connectors: ActionConnector[], actionItem: AlertAction, actionTypesIndex: ActionTypeIndex @@ -49,3 +51,10 @@ export const getEnabledAndConfiguredConnectors = ( (actionType?.enabledInConfig || connector.isPreconfigured) ); }; + +export const deprecatedMessage = i18n.translate( + 'xpack.triggersActionsUI.sections.deprecatedTitleMessage', + { + defaultMessage: '(deprecated)', + } +); From 31ea4568c8b7a23f8a7dff78e60d43c1b67509bf Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Thu, 21 Oct 2021 18:20:01 -0400 Subject: [PATCH 03/18] Each action type can render the dropdown row --- .../servicenow/deprecated_callout.tsx | 36 +++++--- .../servicenow/helpers.ts | 25 +++++- .../servicenow/servicenow.tsx | 2 + .../servicenow/servicenow_connectors.tsx | 14 ++- .../servicenow/servicenow_dropdown_row.tsx | 87 +++++++++++++++++++ .../servicenow/servicenow_sir_params.tsx | 2 + .../action_connector_form/action_form.tsx | 3 +- .../action_type_form.tsx | 1 + .../connector_add_inline.tsx | 1 + .../connector_dropdown.tsx | 50 ++++++----- .../deprecated_connector.tsx | 55 ------------ .../triggers_actions_ui/public/types.ts | 3 + 12 files changed, 186 insertions(+), 93 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.tsx delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/deprecated_connector.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/deprecated_callout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/deprecated_callout.tsx index faeeaa1bbbffe..3aa52f63b4796 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/deprecated_callout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/deprecated_callout.tsx @@ -10,14 +10,35 @@ import { EuiSpacer, EuiCallOut, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +// TODO: add in a link to documentation? interface Props { - onMigrate: () => void; + onMigrate?: () => void; } const DeprecatedCalloutComponent: React.FC = ({ onMigrate }) => { + const update = + onMigrate != null ? ( + + {i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutMigrate', + { + defaultMessage: 'Update this connector,', + } + )} + + ) : ( + + {i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutMigrate', + { + defaultMessage: 'Update this connector,', + } + )} + + ); + return ( <> - = ({ onMigrate }) => { defaultMessage="{update} {create} " id="xpack.triggersActionsUI.components.builtinActionTypes.servicenow.appInstallationInfo" values={{ - update: ( - - {i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutMigrate', - { - defaultMessage: 'Update this connector,', - } - )} - - ), + update, create: ( {i18n.translate( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts index e6acb2e0976a8..3e5e57d8000e4 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts @@ -11,7 +11,11 @@ import { ENABLE_NEW_SN_SIR_CONNECTOR, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../../../actions/server/constants/connectors'; -import { IErrorObject } from '../../../../../public/types'; +import { + ActionConnector, + IErrorObject, + UserConfiguredActionConnector, +} from '../../../../../public/types'; import { AppInfo, Choice, RESTApiError, ServiceNowActionConnector } from './types'; export const DEFAULT_CORRELATION_ID = '{{rule.id}}:{{alert.id}}'; @@ -43,3 +47,22 @@ export const isLegacyConnector = (connector: ServiceNowActionConnector) => { return connector.config.isLegacy; }; + +// TODO: we don't need both of these functions +export const isDeprecatedConnector = (connector: ActionConnector): boolean => { + // TODO: Remove ENABLE_* portion when the applications are certified + if (!ENABLE_NEW_SN_ITSM_CONNECTOR && connector.actionTypeId === '.servicenow') { + return true; + } + + if (!ENABLE_NEW_SN_SIR_CONNECTOR && connector.actionTypeId === '.servicenow-sir') { + return true; + } + + // TODO: add a type guard + const unsafeConfig = ( + connector as UserConfiguredActionConnector, Record> + ).config; + + return !!unsafeConfig?.isLegacy; +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx index 6b6d536ff303b..dd96362d04d0e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx @@ -138,6 +138,7 @@ export function getServiceNowITSMActionType(): ActionTypeModel< return validationResult; }, actionParamsFields: lazy(() => import('./servicenow_itsm_params')), + actionConnectorDropdownComponent: lazy(() => import('./servicenow_dropdown_row')), }; } @@ -174,6 +175,7 @@ export function getServiceNowSIRActionType(): ActionTypeModel< return validationResult; }, actionParamsFields: lazy(() => import('./servicenow_sir_params')), + actionConnectorDropdownComponent: lazy(() => import('./servicenow_dropdown_row')), }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx index 20d38cfc7cea8..86917bc56d5bc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx @@ -7,6 +7,7 @@ import React, { useCallback, useEffect, useState } from 'react'; +import { EuiSpacer } from '@elastic/eui'; import { ActionConnectorFieldsProps } from '../../../../types'; import * as i18n from './translations'; @@ -21,6 +22,9 @@ import { UpdateConnector } from './update_connector'; import { updateActionConnector } from '../../../lib/action_connector_api'; import { Credentials } from './credentials'; +// eslint-disable-next-line import/no-default-export +export { ServiceNowConnectorFields as default }; + const ServiceNowConnectorFields: React.FC> = ({ action, @@ -139,7 +143,7 @@ const ServiceNowConnectorFields: React.FC )} {!isOldConnector && } - {isOldConnector && } + {isOldConnector && } void }) => ( + <> + + + +); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.tsx new file mode 100644 index 0000000000000..a17c3da3eb9ab --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.tsx @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { EuiFlexItem, EuiIconTip } from '@elastic/eui'; +import React from 'react'; +import { i18n } from '@kbn/i18n'; + +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; +import { isDeprecatedConnector } from './helpers'; +import { ActionConnector } from '../../../../types'; +// TODO: move this to a better location +import { preconfiguredMessage } from '../../../sections/action_connector_form/connector_dropdown'; + +// eslint-disable-next-line import/no-default-export +export { ServiceNowSelectableRowComponent as default }; + +function ServiceNowSelectableRowComponent({ + actionConnector, +}: { + actionConnector: ActionConnector; +}) { + const title = getTitle(actionConnector); + + return ( + <> + + {title} + + {isDeprecatedConnector(actionConnector) && ( + + + + )} + + ); +} + +const getTitle = (connector: ActionConnector) => { + let title = connector.name; + + if (connector.isPreconfigured) { + title += ` ${preconfiguredMessage}`; + } + + if (isDeprecatedConnector(connector)) { + title += ` ${deprecatedMessage}`; + } + + return title; +}; + +const StyledIconTip = euiStyled(EuiIconTip)` + margin-left: ${({ theme }) => theme.eui.euiSizeS} + margin-bottom: 0 !important; +`; + +const deprecatedTooltipTitle = i18n.translate( + 'xpack.triggersActionsUI.sections.actionForm.deprecatedTooltipTitle', + { + defaultMessage: 'Deprecated connector', + } +); + +const deprecatedTooltipContent = i18n.translate( + 'xpack.triggersActionsUI.sections.actionForm.deprecatedTooltipContent', + { + defaultMessage: 'Please update your connector', + } +); + +export const deprecatedMessage = i18n.translate( + 'xpack.triggersActionsUI.sections.deprecatedTitleMessage', + { + defaultMessage: '(deprecated)', + } +); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx index 42758250408d9..3052fe11070a0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx @@ -26,6 +26,7 @@ import * as i18n from './translations'; import { useGetChoices } from './use_get_choices'; import { ServiceNowSIRActionParams, Fields, Choice, ServiceNowActionConnector } from './types'; import { choicesToEuiOptions, isLegacyConnector, DEFAULT_CORRELATION_ID } from './helpers'; +import { DeprecatedCallout } from './deprecated_callout'; const useGetChoicesFields = ['category', 'subcategory', 'priority']; const defaultFields: Fields = { @@ -151,6 +152,7 @@ const ServiceNowSIRParamsFields: React.FunctionComponent< return ( <> + {isDeprecatedConnector && }

{i18n.SECURITY_INCIDENT}

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 3dbcc2a66dd23..eda0b99e859a6 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 @@ -40,7 +40,6 @@ import { useKibana } from '../../../common/lib/kibana'; import { DefaultActionParamsGetter } from '../../lib/get_defaults_for_action_params'; import { ConnectorAddModal } from '.'; import { suspendedComponentWithProps } from '../../lib/suspended_component_with_props'; -import { isDeprecatedConnector } from '../common/connectors'; export interface ActionGroupWithMessageVariables extends ActionGroup { omitOptionalMessageVariables?: boolean; @@ -310,7 +309,7 @@ export const ActionForm = ({ actions.map((actionItem: AlertAction, index: number) => { const actionConnector = connectors.find((field) => field.id === actionItem.id); // connectors doesn't exists - if (!actionConnector || isDeprecatedConnector(actionConnector)) { + if (!actionConnector) { return ( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx index 956e6e98bbb5f..966d5ecb7cab3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx @@ -131,6 +131,7 @@ export const AddConnectorInline = ({ actionItem={actionItem} accordionIndex={index} actionTypesIndex={actionTypesIndex} + actionTypeRegistered={actionTypeRegistered} connectors={connectors} onConnectorSelected={onSelectConnector} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx index 6c14a540e20d4..a47c4d465df15 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx @@ -9,14 +9,22 @@ import { EuiFlexGroup, EuiFlexItem, EuiSuperSelect } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; -import { ActionConnector, ActionTypeIndex, AlertAction } from '../../../types'; -import { deprecatedMessage, getValidConnectors, isDeprecatedConnector } from '../common/connectors'; -import { DeprecatedConnectorIcon } from './deprecated_connector'; +import { ActionConnector, ActionTypeIndex, ActionTypeModel, AlertAction } from '../../../types'; +import { getValidConnectors } from '../common/connectors'; + +// TODO: move this to a better location +export const preconfiguredMessage = i18n.translate( + 'xpack.triggersActionsUI.sections.actionForm.preconfiguredTitleMessage', + { + defaultMessage: '(preconfigured)', + } +); export interface DropdownProps { actionItem: AlertAction; accordionIndex: number; actionTypesIndex: ActionTypeIndex; + actionTypeRegistered: ActionTypeModel; connectors: ActionConnector[]; onConnectorSelected: (id: string) => void; } @@ -27,6 +35,7 @@ function ConnectorsDropdownComponent({ actionItem, accordionIndex, actionTypesIndex, + actionTypeRegistered, connectors, onConnectorSelected, }: DropdownProps) { @@ -40,7 +49,10 @@ function ConnectorsDropdownComponent({ [actionItem.id, validConnectors] ); - const options = useMemo(() => createConnectorOptions(validConnectors), [validConnectors]); + const options = useMemo( + () => createConnectorOptions(validConnectors, actionTypeRegistered), + [validConnectors, actionTypeRegistered] + ); const onChange = useCallback((id: string) => onConnectorSelected(id), [onConnectorSelected]); @@ -69,18 +81,27 @@ const getValueOfSelectedConnector = ( return actionItemId; }; -const createConnectorOptions = (connectors: ActionConnector[]) => +const createConnectorOptions = ( + connectors: ActionConnector[], + actionTypeRegistered: ActionTypeModel +) => connectors.map((connector) => { const title = getTitle(connector); + const ConnectorRow = () => + actionTypeRegistered.actionConnectorDropdownComponent != null ? ( + + ) : ( + + {title} + + ); + return { value: connector.id, inputDisplay: ( - - {title} - - + ), 'data-test-subj': `dropdown-connector-${connector.id}`, @@ -94,20 +115,9 @@ const getTitle = (connector: ActionConnector) => { title += ` ${preconfiguredMessage}`; } - if (isDeprecatedConnector(connector)) { - title += ` ${deprecatedMessage}`; - } - return title; }; -const preconfiguredMessage = i18n.translate( - 'xpack.triggersActionsUI.sections.actionForm.preconfiguredTitleMessage', - { - defaultMessage: '(preconfigured)', - } -); - const incidentManagemSystem = i18n.translate( 'xpack.triggersActionsUI.sections.actionForm.incidentManagementSystemLabel', { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/deprecated_connector.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/deprecated_connector.tsx deleted file mode 100644 index cccc3892a4075..0000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/deprecated_connector.tsx +++ /dev/null @@ -1,55 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiFlexItem, EuiIconTip } from '@elastic/eui'; -import React from 'react'; -import { i18n } from '@kbn/i18n'; - -import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; -import { ActionConnector } from '../../../types'; -import { isDeprecatedConnector } from '../common/connectors'; - -export const DeprecatedConnectorIcon = React.memo(DeprecatedConnectorComponent); - -function DeprecatedConnectorComponent({ connector }: { connector: ActionConnector }) { - return ( - <> - {isDeprecatedConnector(connector) && ( - - - - )} - - ); -} - -const StyledIconTip = euiStyled(EuiIconTip)` - margin-left: ${({ theme }) => theme.eui.euiSizeS} - margin-bottom: 0 !important; -`; - -const deprecatedTooltipTitle = i18n.translate( - 'xpack.triggersActionsUI.sections.actionForm.deprecatedTooltipTitle', - { - defaultMessage: 'Deprecated connector', - } -); - -const deprecatedTooltipContent = i18n.translate( - 'xpack.triggersActionsUI.sections.actionForm.deprecatedTooltipContent', - { - defaultMessage: 'Please update your connector', - } -); diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 8085f9245f4e9..d8a733b556065 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -134,6 +134,9 @@ export interface ActionTypeModel > | null; actionParamsFields: React.LazyExoticComponent>>; + actionConnectorDropdownComponent?: React.LazyExoticComponent< + ComponentType<{ actionConnector: ActionConnector }> + >; } export interface GenericValidationResult { From aa7b647ba7bd05ae1d3c3f3d8dc88f695fa1d3c3 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Mon, 25 Oct 2021 11:38:45 -0400 Subject: [PATCH 04/18] Refactoring and fixing todos --- .../servicenow/deprecated_callout.tsx | 24 ++-- .../servicenow/helpers.ts | 49 +------ .../servicenow/servicenow_connectors.tsx | 5 +- .../servicenow/servicenow_dropdown_row.tsx | 12 +- .../servicenow/servicenow_itsm_params.tsx | 11 +- .../servicenow/servicenow_sir_params.tsx | 13 +- .../action_type_form.test.tsx | 1 - .../action_type_form.tsx | 122 +++++++----------- .../connector_dropdown.tsx | 9 +- .../components/actions_connectors_list.tsx | 2 +- .../application/sections/common/connectors.ts | 39 +----- .../public/common/connectors_dropdown.tsx | 54 ++++++++ 12 files changed, 131 insertions(+), 210 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/deprecated_callout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/deprecated_callout.tsx index 3aa52f63b4796..b389a9ac953ad 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/deprecated_callout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/deprecated_callout.tsx @@ -10,7 +10,6 @@ import { EuiSpacer, EuiCallOut, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -// TODO: add in a link to documentation? interface Props { onMigrate?: () => void; } @@ -19,22 +18,10 @@ const DeprecatedCalloutComponent: React.FC = ({ onMigrate }) => { const update = onMigrate != null ? ( - {i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutMigrate', - { - defaultMessage: 'Update this connector,', - } - )} + {updateThisConnectorMessage} ) : ( - - {i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutMigrate', - { - defaultMessage: 'Update this connector,', - } - )} - + {updateThisConnectorMessage} ); return ( @@ -75,3 +62,10 @@ const DeprecatedCalloutComponent: React.FC = ({ onMigrate }) => { }; export const DeprecatedCallout = memo(DeprecatedCalloutComponent); + +const updateThisConnectorMessage = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutMigrate', + { + defaultMessage: 'Update this connector,', + } +); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts index 3e5e57d8000e4..a6230b1c42669 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts @@ -6,17 +6,8 @@ */ import { EuiSelectOption } from '@elastic/eui'; -import { - ENABLE_NEW_SN_ITSM_CONNECTOR, - ENABLE_NEW_SN_SIR_CONNECTOR, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../../actions/server/constants/connectors'; -import { - ActionConnector, - IErrorObject, - UserConfiguredActionConnector, -} from '../../../../../public/types'; -import { AppInfo, Choice, RESTApiError, ServiceNowActionConnector } from './types'; +import { IErrorObject } from '../../../../../public/types'; +import { AppInfo, Choice, RESTApiError } from './types'; export const DEFAULT_CORRELATION_ID = '{{rule.id}}:{{alert.id}}'; @@ -30,39 +21,3 @@ export const isFieldInvalid = ( field: string | undefined | null, error: string | IErrorObject | string[] ): boolean => error !== undefined && error.length > 0 && field != null; - -// TODO: Remove when the applications are certified -export const isLegacyConnector = (connector: ServiceNowActionConnector) => { - if (connector == null) { - return true; - } - - if (!ENABLE_NEW_SN_ITSM_CONNECTOR && connector.actionTypeId === '.servicenow') { - return true; - } - - if (!ENABLE_NEW_SN_SIR_CONNECTOR && connector.actionTypeId === '.servicenow-sir') { - return true; - } - - return connector.config.isLegacy; -}; - -// TODO: we don't need both of these functions -export const isDeprecatedConnector = (connector: ActionConnector): boolean => { - // TODO: Remove ENABLE_* portion when the applications are certified - if (!ENABLE_NEW_SN_ITSM_CONNECTOR && connector.actionTypeId === '.servicenow') { - return true; - } - - if (!ENABLE_NEW_SN_SIR_CONNECTOR && connector.actionTypeId === '.servicenow-sir') { - return true; - } - - // TODO: add a type guard - const unsafeConfig = ( - connector as UserConfiguredActionConnector, Record> - ).config; - - return !!unsafeConfig?.isLegacy; -}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx index 2292ac4f015a8..c7404e2e7d88f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx @@ -16,11 +16,12 @@ import { useKibana } from '../../../../common/lib/kibana'; import { DeprecatedCallout } from './deprecated_callout'; import { useGetAppInfo } from './use_get_app_info'; import { ApplicationRequiredCallout } from './application_required_callout'; -import { isRESTApiError, isLegacyConnector } from './helpers'; +import { isRESTApiError } from './helpers'; import { InstallationCallout } from './installation_callout'; import { UpdateConnector } from './update_connector'; import { updateActionConnector } from '../../../lib/action_connector_api'; import { Credentials } from './credentials'; +import { isDeprecatedConnector } from '../../../../common/connectors_dropdown'; // eslint-disable-next-line import/no-default-export export { ServiceNowConnectorFields as default }; @@ -42,7 +43,7 @@ const ServiceNowConnectorFields: React.FC - {!isDeprecatedConnector && ( + {!isDeprecatedActionConnector && ( <> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx index 3052fe11070a0..0d0a67a243890 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx @@ -24,9 +24,10 @@ import { TextFieldWithMessageVariables } from '../../text_field_with_message_var import * as i18n from './translations'; import { useGetChoices } from './use_get_choices'; -import { ServiceNowSIRActionParams, Fields, Choice, ServiceNowActionConnector } from './types'; -import { choicesToEuiOptions, isLegacyConnector, DEFAULT_CORRELATION_ID } from './helpers'; +import { ServiceNowSIRActionParams, Fields, Choice } from './types'; +import { choicesToEuiOptions, DEFAULT_CORRELATION_ID } from './helpers'; import { DeprecatedCallout } from './deprecated_callout'; +import { isDeprecatedConnector } from '../../../../common/connectors_dropdown'; const useGetChoicesFields = ['category', 'subcategory', 'priority']; const defaultFields: Fields = { @@ -44,9 +45,7 @@ const ServiceNowSIRParamsFields: React.FunctionComponent< notifications: { toasts }, } = useKibana().services; - const isDeprecatedConnector = isLegacyConnector( - actionConnector as unknown as ServiceNowActionConnector - ); + const isDeprecatedActionConnector = isDeprecatedConnector(actionConnector); const actionConnectorRef = useRef(actionConnector?.id ?? ''); const { incident, comments } = useMemo( @@ -152,7 +151,7 @@ const ServiceNowSIRParamsFields: React.FunctionComponent< return ( <> - {isDeprecatedConnector && } + {isDeprecatedActionConnector && }

{i18n.SECURITY_INCIDENT}

@@ -231,7 +230,7 @@ const ServiceNowSIRParamsFields: React.FunctionComponent<
- {!isDeprecatedConnector && ( + {!isDeprecatedActionConnector && ( <> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx index 4bd9032cbc6f5..3f7a0828a85fe 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx @@ -6,7 +6,6 @@ */ import * as React from 'react'; import { mountWithIntl, nextTick } from '@kbn/test/jest'; - import { ActionTypeForm } from './action_type_form'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx index 9a38a3dcf3b95..dd22bfd401e5a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx @@ -35,13 +35,8 @@ import { ActionVariables, ActionTypeRegistryContract, REQUIRED_ACTION_VARIABLES, - ActionTypeModel, } from '../../../types'; -import { - checkActionFormActionTypeEnabled, - IsDisabledResult, - IsEnabledResult, -} from '../../lib/check_action_type_enabled'; +import { checkActionFormActionTypeEnabled } from '../../lib/check_action_type_enabled'; import { hasSaveActionsCapability } from '../../lib/capabilities'; import { ActionAccordionFormProps, ActionGroupWithMessageVariables } from './action_form'; import { transformActionVariables } from '../../lib/action_variables'; @@ -293,12 +288,52 @@ export const ActionTypeForm = ({ buttonContentClassName="actAccordionActionForm__button" data-test-subj={`alertActionAccordion-${index}`} buttonContent={ - + + + + + + +
+ + + + + {selectedActionGroup && !isOpen && ( + + {selectedActionGroup.name} + + )} + + {checkEnabledResult.isEnabled === false && ( + <> + + + )} + + +
+
+
+
} extraAction={ { - return ( - - - - - - -
- - - - - {selectedActionGroup && !isOpen && ( - - {selectedActionGroup.name} - - )} - - {checkEnabledResult.isEnabled === false && ( - <> - - - )} - - -
-
-
-
- ); -}; - function getAvailableActionVariables( actionVariables: ActionVariables, actionGroup?: ActionGroupWithMessageVariables diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx index a47c4d465df15..a408c121f1bbf 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx @@ -11,14 +11,7 @@ import { i18n } from '@kbn/i18n'; import { ActionConnector, ActionTypeIndex, ActionTypeModel, AlertAction } from '../../../types'; import { getValidConnectors } from '../common/connectors'; - -// TODO: move this to a better location -export const preconfiguredMessage = i18n.translate( - 'xpack.triggersActionsUI.sections.actionForm.preconfiguredTitleMessage', - { - defaultMessage: '(preconfigured)', - } -); +import { preconfiguredMessage } from '../../../common/connectors_dropdown'; export interface DropdownProps { actionItem: AlertAction; 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 8a8fefa7ae85c..c6a2e89da200d 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 @@ -47,7 +47,7 @@ import { DEFAULT_HIDDEN_ACTION_TYPES } from '../../../../'; import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner'; import ConnectorEditFlyout from '../../action_connector_form/connector_edit_flyout'; import ConnectorAddFlyout from '../../action_connector_form/connector_add_flyout'; -import { deprecatedMessage, isDeprecatedConnector } from '../../common/connectors'; +import { deprecatedMessage, isDeprecatedConnector } from '../../../../common/connectors_dropdown'; const ConnectorIconTipWithSpacing = withTheme(({ theme }: { theme: EuiTheme }) => { return ( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts index 378fdf206ecdd..4bf7f036ba10a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts @@ -5,37 +5,7 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; - -import { - ENABLE_NEW_SN_ITSM_CONNECTOR, - ENABLE_NEW_SN_SIR_CONNECTOR, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../actions/server/constants/connectors'; -import { - ActionConnector, - ActionTypeIndex, - AlertAction, - UserConfiguredActionConnector, -} from '../../../types'; - -export const isDeprecatedConnector = (connector: ActionConnector): boolean => { - // TODO: Remove ENABLE_* portion when the applications are certified - if (!ENABLE_NEW_SN_ITSM_CONNECTOR && connector.actionTypeId === '.servicenow') { - return true; - } - - if (!ENABLE_NEW_SN_SIR_CONNECTOR && connector.actionTypeId === '.servicenow-sir') { - return true; - } - - // TODO: add a type guard - const unsafeConfig = ( - connector as UserConfiguredActionConnector, Record> - ).config; - - return !!unsafeConfig?.isLegacy; -}; +import { ActionConnector, ActionTypeIndex, AlertAction } from '../../../types'; export const getValidConnectors = ( connectors: ActionConnector[], @@ -51,10 +21,3 @@ export const getValidConnectors = ( (actionType?.enabledInConfig || connector.isPreconfigured) ); }; - -export const deprecatedMessage = i18n.translate( - 'xpack.triggersActionsUI.sections.deprecatedTitleMessage', - { - defaultMessage: '(deprecated)', - } -); diff --git a/x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.tsx b/x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.tsx new file mode 100644 index 0000000000000..b2543db8cd1fc --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.tsx @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { ServiceNowActionConnector } from '../application/components/builtin_action_types/servicenow/types'; +import { ActionConnector, UserConfiguredActionConnector } from '../types'; + +export const preconfiguredMessage = i18n.translate( + 'xpack.triggersActionsUI.sections.actionForm.preconfiguredTitleMessage', + { + defaultMessage: '(preconfigured)', + } +); + +export const deprecatedMessage = i18n.translate( + 'xpack.triggersActionsUI.sections.deprecatedTitleMessage', + { + defaultMessage: '(deprecated)', + } +); + +export const isDeprecatedConnector = ( + connector?: ActionConnector | ServiceNowActionConnector +): boolean => { + if (connector == null) { + return false; + } + + if (isConnectorWithConfig(connector)) { + return !!connector.config.isLegacy; + } + + return false; +}; + +type ConnectorWithUnknownConfig = UserConfiguredActionConnector< + Record, + Record +>; + +const isConnectorWithConfig = ( + connector: ActionConnector | ServiceNowActionConnector +): connector is ConnectorWithUnknownConfig => { + const unsafeConnector = connector as UserConfiguredActionConnector< + Record, + Record + >; + + return unsafeConnector.config != null; +}; From 0403c1500dfdb763156bbb75fdc03bea55de2b84 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Mon, 25 Oct 2021 15:50:30 -0400 Subject: [PATCH 05/18] Fixing jest tests --- .../action_type_form.test.tsx | 222 ++++++------------ 1 file changed, 71 insertions(+), 151 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx index 3f7a0828a85fe..e8590595b9d61 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx @@ -17,7 +17,6 @@ import { } from '../../../types'; import { act } from 'react-dom/test-utils'; import { EuiFieldText } from '@elastic/eui'; -import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common'; import { DefaultActionParams } from '../../lib/get_defaults_for_action_params'; jest.mock('../../../common/lib/kibana'); @@ -39,7 +38,7 @@ describe('action_type_form', () => { }, })); - beforeEach(() => { + it('calls "setActionParamsProperty" to set the default value for the empty dedupKey', async () => { const actionType = actionTypeRegistryMock.createMockActionTypeModel({ id: '.pagerduty', iconClass: 'test', @@ -55,101 +54,22 @@ describe('action_type_form', () => { actionParamsFields: mockedActionParamsFields, }); actionTypeRegistry.get.mockReturnValue(actionType); - }); - - describe('deprecated icon', () => { - const notDeprecatedConnector = { - actionTypeId: '.pagerduty', - config: { - apiUrl: 'http:\\test', - }, - id: 'test', - isPreconfigured: false, - name: 'test name', - secrets: {}, - }; - - const actionItem = { - id: 'test', - actionTypeId: '.pagerduty', - group: 'recovered', - params: { - eventAction: 'recovered', - dedupKey: undefined, - summary: '2323', - source: 'source', - severity: '1', - timestamp: new Date().toISOString(), - component: 'test', - group: 'group', - class: 'test class', - }, - }; - - it('renders the deprecated warning icon for deprecated connectors', async () => { - const deprecatedConnector = { - ...notDeprecatedConnector, - config: { ...notDeprecatedConnector.config, isLegacy: true }, - }; - - const wrapper = mountWithIntl( - getActionTypeForm({ - actionConnector: deprecatedConnector, - connectors: [deprecatedConnector], - actionItem, - }) - ); - - // Wait for active space to resolve before requesting the component to update - await act(async () => { - await nextTick(); - wrapper.update(); - }); - - expect( - wrapper.find('[data-test-subj="deprecated-connector-icon-test"]').exists() - ).toBeTruthy(); - }); - - it('does not render the deprecated warning icon for non-deprecated connectors', async () => { - const wrapper = mountWithIntl( - getActionTypeForm({ - actionConnector: notDeprecatedConnector, - connectors: [notDeprecatedConnector], - actionItem, - }) - ); - - // Wait for active space to resolve before requesting the component to update - await act(async () => { - await nextTick(); - wrapper.update(); - }); - - expect( - wrapper.find('[data-test-subj="deprecated-connector-icon-test"]').exists() - ).toBeFalsy(); - }); - }); - it('calls "setActionParamsProperty" to set the default value for the empty dedupKey', async () => { const wrapper = mountWithIntl( - getActionTypeForm({ - actionItem: { - id: '123', - actionTypeId: '.pagerduty', - group: 'recovered', - params: { - eventAction: 'recovered', - dedupKey: undefined, - summary: '2323', - source: 'source', - severity: '1', - timestamp: new Date().toISOString(), - component: 'test', - group: 'group', - class: 'test class', - }, + getActionTypeForm(1, undefined, { + id: '123', + actionTypeId: '.pagerduty', + group: 'recovered', + params: { + eventAction: 'recovered', + dedupKey: undefined, + summary: '2323', + source: 'source', + severity: '1', + timestamp: new Date().toISOString(), + component: 'test', + group: 'group', + class: 'test class', }, }) ); @@ -168,24 +88,37 @@ describe('action_type_form', () => { }); it('does not call "setActionParamsProperty" because dedupKey is not empty', async () => { + const actionType = actionTypeRegistryMock.createMockActionTypeModel({ + id: '.pagerduty', + iconClass: 'test', + selectMessage: 'test', + validateConnector: (): Promise> => { + return Promise.resolve({}); + }, + validateParams: (): Promise> => { + const validationResult = { errors: {} }; + return Promise.resolve(validationResult); + }, + actionConnectorFields: null, + actionParamsFields: mockedActionParamsFields, + }); + actionTypeRegistry.get.mockReturnValue(actionType); + const wrapper = mountWithIntl( - getActionTypeForm({ - index: 1, - actionItem: { - id: '123', - actionTypeId: '.pagerduty', - group: 'recovered', - params: { - eventAction: 'recovered', - dedupKey: '232323', - summary: '2323', - source: 'source', - severity: '1', - timestamp: new Date().toISOString(), - component: 'test', - group: 'group', - class: 'test class', - }, + getActionTypeForm(1, undefined, { + id: '123', + actionTypeId: '.pagerduty', + group: 'recovered', + params: { + eventAction: 'recovered', + dedupKey: '232323', + summary: '2323', + source: 'source', + severity: '1', + timestamp: new Date().toISOString(), + component: 'test', + group: 'group', + class: 'test class', }, }) ); @@ -202,29 +135,18 @@ describe('action_type_form', () => { }); }); -function getActionTypeForm({ - index, - actionConnector, - actionItem, - defaultActionGroupId, - connectors, - actionTypeIndex, - defaultParams, - onAddConnector, - onDeleteAction, - onConnectorSelected, -}: { - index?: number; - actionConnector?: ActionConnector, Record>; - actionItem?: AlertAction; - defaultActionGroupId?: string; - connectors?: Array, Record>>; - actionTypeIndex?: Record; - defaultParams?: DefaultActionParams; - onAddConnector?: () => void; - onDeleteAction?: () => void; - onConnectorSelected?: (id: string) => void; -}) { +function getActionTypeForm( + index?: number, + actionConnector?: ActionConnector, Record>, + actionItem?: AlertAction, + defaultActionGroupId?: string, + connectors?: Array, Record>>, + actionTypeIndex?: Record, + defaultParams?: DefaultActionParams, + onAddConnector?: () => void, + onDeleteAction?: () => void, + onConnectorSelected?: (id: string) => void +) { const actionConnectorDefault = { actionTypeId: '.pagerduty', config: { @@ -291,21 +213,19 @@ function getActionTypeForm({ eventAction: 'resolve', }; return ( - - - + ); } From 2cbbaced9d60f3e7aa7a47e55a7a2c714f3f98fd Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Mon, 25 Oct 2021 15:50:45 -0400 Subject: [PATCH 06/18] Adding and fixing other tests --- .../servicenow_dropdown_row.test.tsx | 71 +++++++ .../action_form.test.tsx | 64 +++--- .../connector_add_inline.test.tsx | 192 ------------------ .../connector_dropdown.test.tsx | 128 ++++++++++++ .../connector_dropdown.tsx | 2 +- 5 files changed, 241 insertions(+), 216 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.test.tsx delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.test.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.test.tsx new file mode 100644 index 0000000000000..4597f655183b9 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { mountWithIntl } from '@kbn/test/jest'; + +import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common'; +import { ActionConnector } from '../../../../types'; +import ServiceNowSelectableRowComponent from './servicenow_dropdown_row'; + +const connector: ActionConnector = { + secrets: {}, + config: { + isLegacy: true, + }, + id: 'test', + actionTypeId: '.test', + name: 'Test', + isPreconfigured: false, +}; + +describe('ServiceNowSelectableRowComponent', () => { + it('adds deprecated to the connector title when its config is marked as deprecated', () => { + render( + + + + ); + + expect(screen.queryByText('Test (deprecated)')).toBeInTheDocument(); + }); + + it('renders an icon marking the connector as deprecated when its config is marked as deprecated', () => { + const wrapper = mountWithIntl( + + + + ); + + expect(wrapper.find('[data-test-subj="deprecated-connector-icon-test"]').exists()).toBeTruthy(); + }); + + it('does not add the deprecated text to the connector title when it is not deprecated', () => { + const nonDeprecatedConnector = { ...connector, config: { isLegacy: false } }; + + render( + + + + ); + + expect(screen.queryByText('Test')).toBeInTheDocument(); + }); + + it('does not render an icon marking the connector as deprecated when its config is marked as not deprecated', () => { + const nonDeprecatedConnector = { ...connector, config: { isLegacy: false } }; + + const wrapper = mountWithIntl( + + + + ); + + expect(wrapper.find('[data-test-subj="deprecated-connector-icon-test"]').exists()).toBeFalsy(); + }); +}); 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 6e6c6aa52b3e3..268db7d6c69af 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 @@ -483,7 +483,7 @@ describe('action_form', () => { `[data-test-subj="${actionType.id}-ActionTypeSelectOption"]` ); actionOption.first().simulate('click'); - const combobox = wrapper.find(`[data-test-subj="selectActionConnector-${actionType.id}"]`); + const combobox = wrapper.find(`[data-test-subj="selectActionConnector-${actionType.id}-0"]`); const numConnectors = allActions.filter( (action) => action.actionTypeId === actionType.id ).length; @@ -494,35 +494,53 @@ describe('action_form', () => { numConnectors - numConnectorsWithMissingSecrets ); expect((combobox.first().props() as any).options).toMatchInlineSnapshot(` - Array [ - Object { - "id": "test", - "key": "test", - "label": "Test connector ", - }, - Object { - "id": "test2", - "key": "test2", - "label": "Test connector 2 (preconfigured)", - }, - ] - `); + Array [ + Object { + "data-test-subj": "dropdown-connector-test", + "inputDisplay": + + , + "value": "test", + }, + Object { + "data-test-subj": "dropdown-connector-test2", + "inputDisplay": + + , + "value": "test2", + }, + ] + `); }); it('renders only preconfigured connectors for the selected preconfigured action type', async () => { const wrapper = await setup(); const actionOption = wrapper.find('[data-test-subj="preconfigured-ActionTypeSelectOption"]'); actionOption.first().simulate('click'); - const combobox = wrapper.find('[data-test-subj="selectActionConnector-preconfigured"]'); + const combobox = wrapper.find('[data-test-subj="selectActionConnector-preconfigured-1"]'); expect((combobox.first().props() as any).options).toMatchInlineSnapshot(` - Array [ - Object { - "id": "test3", - "key": "test3", - "label": "Preconfigured Only (preconfigured)", - }, - ] - `); + Array [ + Object { + "data-test-subj": "dropdown-connector-test3", + "inputDisplay": + + , + "value": "test3", + }, + ] + `); }); it('does not render "Add connector" button for preconfigured only action type', async () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.test.tsx deleted file mode 100644 index ee2735fc95e87..0000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.test.tsx +++ /dev/null @@ -1,192 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { mountWithIntl, nextTick } from '@kbn/test/jest'; - -import { actionTypeRegistryMock } from '../../action_type_registry.mock'; -import { - ActionConnector, - ActionType, - AlertAction, - ConnectorValidationResult, - GenericValidationResult, -} from '../../../types'; -import { act } from 'react-dom/test-utils'; -import { EuiFieldText } from '@elastic/eui'; -import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common'; -import AddConnectorInline from './connector_add_inline'; -import { useKibana } from '../../../common/lib/kibana'; -import { coreMock } from '../../../../../../../src/core/public/mocks'; - -jest.mock('../../../common/lib/kibana'); -const useKibanaMock = useKibana as jest.Mocked; -const actionTypeRegistry = actionTypeRegistryMock.create(); -const mocks = coreMock.createSetup(); - -describe('connector_add_inline', () => { - const mockedActionParamsFields = React.lazy(async () => ({ - default() { - return ( - <> - true} - fullWidth - /> - - ); - }, - })); - - beforeEach(async () => { - const [ - { - application: { capabilities }, - }, - ] = await mocks.getStartServices(); - useKibanaMock().services.application.capabilities = { - ...capabilities, - actions: { - delete: true, - save: true, - show: true, - }, - }; - const actionType = actionTypeRegistryMock.createMockActionTypeModel({ - id: '.pagerduty', - iconClass: 'test', - selectMessage: 'test', - validateConnector: (): Promise> => { - return Promise.resolve({}); - }, - validateParams: (): Promise> => { - const validationResult = { errors: {} }; - return Promise.resolve(validationResult); - }, - actionConnectorFields: null, - actionParamsFields: mockedActionParamsFields, - actionTypeTitle: 'pagerduty', - }); - actionTypeRegistry.get.mockReturnValue(actionType); - }); - - describe('deprecated icon', () => { - const notDeprecatedConnector = { - actionTypeId: '.pagerduty', - config: { - apiUrl: 'http:\\test', - }, - id: '123', - isPreconfigured: true, - name: 'test name', - secrets: {}, - }; - - const actionItem = { - id: '123', - actionTypeId: '.pagerduty', - group: 'recovered', - params: { - eventAction: 'recovered', - dedupKey: undefined, - summary: '2323', - source: 'source', - severity: '1', - timestamp: new Date().toISOString(), - component: 'test', - group: 'group', - class: 'test class', - }, - }; - - it('renders the deprecated warning icon for deprecated connectors', async () => { - const deprecatedConnector = { - ...notDeprecatedConnector, - config: { ...notDeprecatedConnector.config, isLegacy: true }, - }; - - const wrapper = mountWithIntl( - createConnectorAddInlineComponent({ - connectors: [deprecatedConnector], - actionItem, - }) - ); - - // Wait for active space to resolve before requesting the component to update - await act(async () => { - await nextTick(); - wrapper.update(); - }); - - expect( - wrapper.find('[data-test-subj="deprecated-connector-icon-123"]').exists() - ).toBeTruthy(); - }); - - it('does not render the deprecated warning icon for non-deprecated connectors', async () => { - const wrapper = mountWithIntl( - createConnectorAddInlineComponent({ - connectors: [notDeprecatedConnector], - actionItem, - }) - ); - - // Wait for active space to resolve before requesting the component to update - await act(async () => { - await nextTick(); - wrapper.update(); - }); - - expect(wrapper.find('[data-test-subj="deprecated-connector-icon-123"]').exists()).toBeFalsy(); - }); - }); -}); - -function createConnectorAddInlineComponent({ - actionItem, - connectors, -}: { - actionItem: AlertAction; - connectors: ActionConnector[]; -}) { - const actionTypeIndex: Record = { - '.pagerduty': { - id: '.pagerduty', - enabled: true, - name: 'Test', - enabledInConfig: true, - enabledInLicense: true, - minimumLicenseRequired: 'basic', - }, - '.server-log': { - id: '.server-log', - enabled: true, - name: 'Test SL', - enabledInConfig: true, - enabledInLicense: true, - minimumLicenseRequired: 'basic', - }, - }; - - return ( - - - - ); -} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.test.tsx new file mode 100644 index 0000000000000..bf70d81174727 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.test.tsx @@ -0,0 +1,128 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as React from 'react'; +import { render, screen } from '@testing-library/react'; +import { mountWithIntl } from '@kbn/test/jest'; +import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common'; +import { ConnectorsDropdown } from './connector_dropdown'; +import { actionTypeRegistryMock } from '../../action_type_registry.mock'; +import { ActionType, ConnectorValidationResult, GenericValidationResult } from '../../../types'; +import { EuiFieldText } from '@elastic/eui'; + +describe('connector_dropdown', () => { + const mockedActionParamsFields = React.lazy(async () => ({ + default() { + return ( + <> + true} + fullWidth + /> + + ); + }, + })); + + const actionItem = { + id: 'testId', + actionTypeId: '.pagerduty', + group: 'recovered', + params: { + eventAction: 'recovered', + dedupKey: undefined, + summary: '2323', + source: 'source', + severity: '1', + timestamp: new Date().toISOString(), + component: 'test', + group: 'group', + class: 'test class', + }, + }; + + const actionTypeIndex: Record = { + '.pagerduty': { + id: '.pagerduty', + enabled: true, + name: 'Test', + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'basic', + }, + }; + + const connectors = [ + { + actionTypeId: '.pagerduty', + config: { + apiUrl: 'http:\\test', + }, + id: 'testId', + isPreconfigured: false, + name: 'test pagerduty', + secrets: {}, + }, + ]; + + const actionType = actionTypeRegistryMock.createMockActionTypeModel({ + id: '.pagerduty', + iconClass: 'test', + selectMessage: 'test', + validateConnector: (): Promise> => { + return Promise.resolve({}); + }, + validateParams: (): Promise> => { + const validationResult = { errors: {} }; + return Promise.resolve(validationResult); + }, + actionConnectorFields: null, + actionParamsFields: mockedActionParamsFields, + }); + + beforeEach(() => {}); + + it('renders a super selector', () => { + const wrapper = mountWithIntl( + + + + ); + + expect( + wrapper.find('[data-test-subj="selectActionConnector-.pagerduty-0"]').exists() + ).toBeTruthy(); + }); + + it('renders the title of the connector', () => { + render( + + + + ); + + // there should be two, the first is because it is the default selected row, and then another + // as the row option + expect(screen.queryAllByText('test pagerduty')).toHaveLength(2); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx index a408c121f1bbf..a11397eefe414 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx @@ -13,7 +13,7 @@ import { ActionConnector, ActionTypeIndex, ActionTypeModel, AlertAction } from ' import { getValidConnectors } from '../common/connectors'; import { preconfiguredMessage } from '../../../common/connectors_dropdown'; -export interface DropdownProps { +interface DropdownProps { actionItem: AlertAction; accordionIndex: number; actionTypesIndex: ActionTypeIndex; From 6c716283ce03256f56c34f789f0bd54bef1678f1 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Mon, 25 Oct 2021 17:41:33 -0400 Subject: [PATCH 07/18] Fixing functional test --- .../action_connector_form/connector_dropdown.test.tsx | 2 +- .../apps/triggers_actions_ui/details.ts | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.test.tsx index bf70d81174727..20b8cb23eea4f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.test.tsx @@ -113,7 +113,7 @@ describe('connector_dropdown', () => { { expect(await testSubjects.exists('addNewActionConnectorActionGroup-0')).to.eql(false); expect(await testSubjects.exists('alertActionAccordion-0')).to.eql(true); - await comboBox.set('selectActionConnector-.slack-0', 'Slack#xyztest (preconfigured)'); + expect(await testSubjects.exists('selectActionConnector-.slack-0')).to.eql(true); + // click the super selector the reveal the options + await testSubjects.click('selectActionConnector-.slack-0'); + // click the available option (my-slack1 is a preconfigured connector created before this test runs) + await testSubjects.click('dropdown-connector-my-slack1'); expect(await testSubjects.exists('addNewActionConnectorActionGroup-0')).to.eql(true); }); From 2d60ec61d3d900d559c8e8727c7540a9db8d2dd1 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Tue, 26 Oct 2021 17:06:37 -0400 Subject: [PATCH 08/18] Fixing tests --- .../servicenow/servicenow_dropdown_row.test.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.test.tsx index 4597f655183b9..ef182736ba4df 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.test.tsx @@ -16,7 +16,7 @@ import ServiceNowSelectableRowComponent from './servicenow_dropdown_row'; const connector: ActionConnector = { secrets: {}, config: { - isLegacy: true, + usesTableApi: true, }, id: 'test', actionTypeId: '.test', @@ -46,7 +46,7 @@ describe('ServiceNowSelectableRowComponent', () => { }); it('does not add the deprecated text to the connector title when it is not deprecated', () => { - const nonDeprecatedConnector = { ...connector, config: { isLegacy: false } }; + const nonDeprecatedConnector = { ...connector, config: { usesTableApi: false } }; render( @@ -58,7 +58,7 @@ describe('ServiceNowSelectableRowComponent', () => { }); it('does not render an icon marking the connector as deprecated when its config is marked as not deprecated', () => { - const nonDeprecatedConnector = { ...connector, config: { isLegacy: false } }; + const nonDeprecatedConnector = { ...connector, config: { usesTableApi: false } }; const wrapper = mountWithIntl( From 0e462f653aafe946c212f5f30eabbfd1bfdd8fa6 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Wed, 27 Oct 2021 14:38:24 -0400 Subject: [PATCH 09/18] Adjusting text to match cases --- .../servicenow/servicenow_dropdown_row.tsx | 15 +++++---------- .../components/actions_connectors_list.tsx | 11 ++++++----- .../public/common/connectors_dropdown.tsx | 5 +++++ 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.tsx index 7ffc797f5de60..1dc03e954bb93 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.tsx @@ -9,7 +9,10 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; -import { isDeprecatedConnector } from '../../../../common/connectors_dropdown'; +import { + connectorDeprecatedMessage, + isDeprecatedConnector, +} from '../../../../common/connectors_dropdown'; import { ActionConnector } from '../../../../types'; import { deprecatedMessage, preconfiguredMessage } from '../../../../common/connectors_dropdown'; @@ -35,8 +38,7 @@ function ServiceNowSelectableRowComponent({ size={'m'} type="alert" color="warning" - title={deprecatedTooltipTitle} - content={deprecatedTooltipContent} + content={connectorDeprecatedMessage} data-test-subj={`deprecated-connector-icon-${actionConnector?.id}`} />
@@ -70,10 +72,3 @@ const deprecatedTooltipTitle = i18n.translate( defaultMessage: 'Deprecated connector', } ); - -const deprecatedTooltipContent = i18n.translate( - 'xpack.triggersActionsUI.sections.actionForm.deprecatedTooltipContent', - { - defaultMessage: 'Please update your connector', - } -); 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 9c668d50aecc4..3b58a44e03e46 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 @@ -47,7 +47,11 @@ import { DEFAULT_HIDDEN_ACTION_TYPES } from '../../../../'; import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner'; import ConnectorEditFlyout from '../../action_connector_form/connector_edit_flyout'; import ConnectorAddFlyout from '../../action_connector_form/connector_add_flyout'; -import { deprecatedMessage, isDeprecatedConnector } from '../../../../common/connectors_dropdown'; +import { + connectorDeprecatedMessage, + deprecatedMessage, + isDeprecatedConnector, +} from '../../../../common/connectors_dropdown'; const ConnectorIconTipWithSpacing = withTheme(({ theme }: { theme: EuiTheme }) => { return ( @@ -65,10 +69,7 @@ const ConnectorIconTipWithSpacing = withTheme(({ theme }: { theme: EuiTheme }) = size="m" type="alert" color="warning" - content={i18n.translate( - 'xpack.triggersActionsUI.sections.actionsConnectorsList.connectorsListTable.columns.actions.isDeprecatedDescription', - { defaultMessage: 'This connector is deprecated. Update it, or create a new one.' } - )} + content={connectorDeprecatedMessage} position="right" /> )} diff --git a/x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.tsx b/x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.tsx index 74f31ec795562..c7af8bcdcae14 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.tsx @@ -23,6 +23,11 @@ export const deprecatedMessage = i18n.translate( } ); +export const connectorDeprecatedMessage = i18n.translate( + 'xpack.triggersActionsUI.sections.isDeprecatedDescription', + { defaultMessage: 'This connector is deprecated. Update it, or create a new one.' } +); + export const isDeprecatedConnector = ( connector?: ActionConnector | ServiceNowActionConnector ): boolean => { From 45079ef1e60615369ecfb17d4b4d9021038f20f3 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Mon, 15 Nov 2021 13:17:26 -0500 Subject: [PATCH 10/18] Fixing tests --- .../servicenow/helpers.test.ts | 49 +-------------- .../servicenow/servicenow_connectors.tsx | 4 +- .../servicenow/servicenow_dropdown_row.tsx | 6 +- .../servicenow/servicenow_itsm_params.tsx | 4 +- .../servicenow/servicenow_sir_params.tsx | 4 +- .../components/actions_connectors_list.tsx | 6 +- .../common/connectors_dropdown.test.tsx | 59 +++++++++++++++++++ .../public/common/connectors_dropdown.tsx | 2 +- 8 files changed, 73 insertions(+), 61 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.test.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.test.ts index 9a8094f53d501..e40db85bcb12d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { isRESTApiError, isFieldInvalid, isDeprecatedConnector } from './helpers'; +import { isRESTApiError, isFieldInvalid } from './helpers'; describe('helpers', () => { describe('isRESTApiError', () => { @@ -48,51 +48,4 @@ describe('helpers', () => { expect(isFieldInvalid('description', [])).toBeFalsy(); }); }); - - describe('isDeprecatedConnector', () => { - const connector = { - id: 'test', - actionTypeId: '.webhook', - name: 'Test', - config: { apiUrl: 'http://example.com', usesTableApi: false }, - secrets: { username: 'test', password: 'test' }, - isPreconfigured: false as const, - }; - - it('returns false if the connector is not defined', () => { - expect(isDeprecatedConnector()).toBe(false); - }); - - it('returns false if the connector is not ITSM or SecOps', () => { - expect(isDeprecatedConnector(connector)).toBe(false); - }); - - it('returns false if the connector is .servicenow and the usesTableApi=false', () => { - expect(isDeprecatedConnector({ ...connector, actionTypeId: '.servicenow' })).toBe(false); - }); - - it('returns false if the connector is .servicenow-sir and the usesTableApi=false', () => { - expect(isDeprecatedConnector({ ...connector, actionTypeId: '.servicenow-sir' })).toBe(false); - }); - - it('returns true if the connector is .servicenow and the usesTableApi=true', () => { - expect( - isDeprecatedConnector({ - ...connector, - actionTypeId: '.servicenow', - config: { ...connector.config, usesTableApi: true }, - }) - ).toBe(true); - }); - - it('returns true if the connector is .servicenow-sir and the usesTableApi=true', () => { - expect( - isDeprecatedConnector({ - ...connector, - actionTypeId: '.servicenow-sir', - config: { ...connector.config, usesTableApi: true }, - }) - ).toBe(true); - }); - }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx index a003b7be9d46f..f91a482a2d091 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx @@ -21,7 +21,7 @@ import { InstallationCallout } from './installation_callout'; import { UpdateConnector } from './update_connector'; import { updateActionConnector } from '../../../lib/action_connector_api'; import { Credentials } from './credentials'; -import { isDeprecatedConnector } from '../../../../common/connectors_dropdown'; +import { checkConnectorIsDeprecated } from '../../../../common/connectors_dropdown'; // eslint-disable-next-line import/no-default-export export { ServiceNowConnectorFields as default }; @@ -46,7 +46,7 @@ const ServiceNowConnectorFields: React.FC {title} - {isDeprecatedConnector(actionConnector) && ( + {checkConnectorIsDeprecated(actionConnector) && ( { title += ` ${preconfiguredMessage}`; } - if (isDeprecatedConnector(connector)) { + if (checkConnectorIsDeprecated(connector)) { title += ` ${deprecatedMessage}`; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itsm_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itsm_params.tsx index 58b3175543f16..aec0aac3a2950 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itsm_params.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itsm_params.tsx @@ -26,7 +26,7 @@ import { useGetChoices } from './use_get_choices'; import { choicesToEuiOptions, DEFAULT_CORRELATION_ID } from './helpers'; import * as i18n from './translations'; -import { isDeprecatedConnector } from '../../../../common/connectors_dropdown'; +import { checkConnectorIsDeprecated } from '../../../../common/connectors_dropdown'; const useGetChoicesFields = ['urgency', 'severity', 'impact', 'category', 'subcategory']; const defaultFields: Fields = { @@ -47,7 +47,7 @@ const ServiceNowParamsFields: React.FunctionComponent< notifications: { toasts }, } = useKibana().services; - const isDeprecatedActionConnector = isDeprecatedConnector(actionConnector); + const isDeprecatedActionConnector = checkConnectorIsDeprecated(actionConnector); const actionConnectorRef = useRef(actionConnector?.id ?? ''); const { incident, comments } = useMemo( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx index 0d0a67a243890..f8308b612f448 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx @@ -27,7 +27,7 @@ import { useGetChoices } from './use_get_choices'; import { ServiceNowSIRActionParams, Fields, Choice } from './types'; import { choicesToEuiOptions, DEFAULT_CORRELATION_ID } from './helpers'; import { DeprecatedCallout } from './deprecated_callout'; -import { isDeprecatedConnector } from '../../../../common/connectors_dropdown'; +import { checkConnectorIsDeprecated } from '../../../../common/connectors_dropdown'; const useGetChoicesFields = ['category', 'subcategory', 'priority']; const defaultFields: Fields = { @@ -45,7 +45,7 @@ const ServiceNowSIRParamsFields: React.FunctionComponent< notifications: { toasts }, } = useKibana().services; - const isDeprecatedActionConnector = isDeprecatedConnector(actionConnector); + const isDeprecatedActionConnector = checkConnectorIsDeprecated(actionConnector); const actionConnectorRef = useRef(actionConnector?.id ?? ''); const { incident, comments } = useMemo( 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 e15b6effe112a..97b64ebfecbde 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 @@ -50,7 +50,7 @@ import ConnectorAddFlyout from '../../action_connector_form/connector_add_flyout import { connectorDeprecatedMessage, deprecatedMessage, - isDeprecatedConnector, + checkConnectorIsDeprecated, } from '../../../../common/connectors_dropdown'; const ConnectorIconTipWithSpacing = withTheme(({ theme }: { theme: EuiTheme }) => { @@ -203,7 +203,7 @@ const ActionsConnectorsList: React.FunctionComponent = () => { * TODO: Remove when connectors can provide their own UX message. * Issue: https://github.com/elastic/kibana/issues/114507 */ - const showDeprecatedTooltip = isDeprecatedConnector(item); + const showDeprecatedTooltip = checkConnectorIsDeprecated(item); const name = getConnectorName(value, item); const link = ( @@ -490,7 +490,7 @@ function getActionsCountByActionType(actions: ActionConnector[], actionTypeId: s } function getConnectorName(name: string, connector: ActionConnector): string { - return isDeprecatedConnector(connector) ? `${name} ${deprecatedMessage}` : name; + return checkConnectorIsDeprecated(connector) ? `${name} ${deprecatedMessage}` : name; } const DeleteOperation: React.FunctionComponent<{ diff --git a/x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.test.tsx b/x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.test.tsx new file mode 100644 index 0000000000000..9f1c9e4fcc60b --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { checkConnectorIsDeprecated } from './connectors_dropdown'; + +describe('Connectors select', () => { + describe('isDeprecatedConnector', () => { + const connector = { + id: 'test', + actionTypeId: '.webhook', + name: 'Test', + config: { apiUrl: 'http://example.com', usesTableApi: false }, + secrets: { username: 'test', password: 'test' }, + isPreconfigured: false as const, + }; + + it('returns false if the connector is not defined', () => { + expect(checkConnectorIsDeprecated()).toBe(false); + }); + + it('returns false if the connector is not ITSM or SecOps', () => { + expect(checkConnectorIsDeprecated(connector)).toBe(false); + }); + + it('returns false if the connector is .servicenow and the usesTableApi=false', () => { + expect(checkConnectorIsDeprecated({ ...connector, actionTypeId: '.servicenow' })).toBe(false); + }); + + it('returns false if the connector is .servicenow-sir and the usesTableApi=false', () => { + expect(checkConnectorIsDeprecated({ ...connector, actionTypeId: '.servicenow-sir' })).toBe( + false + ); + }); + + it('returns true if the connector is .servicenow and the usesTableApi=true', () => { + expect( + checkConnectorIsDeprecated({ + ...connector, + actionTypeId: '.servicenow', + config: { ...connector.config, usesTableApi: true }, + }) + ).toBe(true); + }); + + it('returns true if the connector is .servicenow-sir and the usesTableApi=true', () => { + expect( + checkConnectorIsDeprecated({ + ...connector, + actionTypeId: '.servicenow-sir', + config: { ...connector.config, usesTableApi: true }, + }) + ).toBe(true); + }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.tsx b/x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.tsx index 3b9f3981f6f60..334a10c95e112 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.tsx @@ -28,7 +28,7 @@ export const connectorDeprecatedMessage = i18n.translate( { defaultMessage: 'This connector is deprecated. Update it, or create a new one.' } ); -export const isDeprecatedConnector = ( +export const checkConnectorIsDeprecated = ( connector?: ActionConnector | ServiceNowActionConnector ): boolean => { if (connector == null) { From 5ba6fa11c79412b9024bafaff5b12c1a061a0887 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Mon, 22 Nov 2021 11:42:17 -0500 Subject: [PATCH 11/18] Addressing pr feedback --- x-pack/plugins/triggers_actions_ui/README.md | 3 +++ .../builtin_action_types/servicenow/servicenow.tsx | 4 ++-- .../servicenow/servicenow_connectors.tsx | 2 +- .../servicenow/servicenow_itsm_params.tsx | 2 +- ...down_row.test.tsx => servicenow_selection_row.test.tsx} | 2 +- ...cenow_dropdown_row.tsx => servicenow_selection_row.tsx} | 7 ++++--- .../servicenow/servicenow_sir_params.tsx | 2 +- .../sections/action_connector_form/connector_dropdown.tsx | 2 +- .../components/actions_connectors_list.tsx | 2 +- ...ors_dropdown.test.tsx => connectors_seleciton.test.tsx} | 2 +- .../{connectors_dropdown.tsx => connectors_selection.tsx} | 0 x-pack/plugins/triggers_actions_ui/public/types.ts | 2 +- 12 files changed, 17 insertions(+), 13 deletions(-) rename x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/{servicenow_dropdown_row.test.tsx => servicenow_selection_row.test.tsx} (96%) rename x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/{servicenow_dropdown_row.tsx => servicenow_selection_row.tsx} (92%) rename x-pack/plugins/triggers_actions_ui/public/common/{connectors_dropdown.test.tsx => connectors_seleciton.test.tsx} (96%) rename x-pack/plugins/triggers_actions_ui/public/common/{connectors_dropdown.tsx => connectors_selection.tsx} (100%) diff --git a/x-pack/plugins/triggers_actions_ui/README.md b/x-pack/plugins/triggers_actions_ui/README.md index d4967552080fe..c5b0daf76e28f 100644 --- a/x-pack/plugins/triggers_actions_ui/README.md +++ b/x-pack/plugins/triggers_actions_ui/README.md @@ -1117,6 +1117,8 @@ Each action type should be defined as an `ActionTypeModel` object with the follo validateParams: (actionParams: any) => Promise; actionConnectorFields: React.FunctionComponent | null; actionParamsFields: React.LazyExoticComponent>>; + customConnectorSelectItemComponent?: React. + LazyExoticComponent ``` |Property|Description| |---|---| @@ -1127,6 +1129,7 @@ Each action type should be defined as an `ActionTypeModel` object with the follo |validateParams|Validation function for action params.| |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.| +|customConnectorSelectItemComponent|Optional, a lazy loaded React component for customizing the selection row of the action connector form.| ## Register action type model diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx index 9cee2e7b25929..eac950c9c58e3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx @@ -138,7 +138,7 @@ export function getServiceNowITSMActionType(): ActionTypeModel< return validationResult; }, actionParamsFields: lazy(() => import('./servicenow_itsm_params')), - actionConnectorDropdownComponent: lazy(() => import('./servicenow_dropdown_row')), + customConnectorSelectItemComponent: lazy(() => import('./servicenow_selection_row')), }; } @@ -175,7 +175,7 @@ export function getServiceNowSIRActionType(): ActionTypeModel< return validationResult; }, actionParamsFields: lazy(() => import('./servicenow_sir_params')), - actionConnectorDropdownComponent: lazy(() => import('./servicenow_dropdown_row')), + customConnectorSelectItemComponent: lazy(() => import('./servicenow_selection_row')), }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx index f91a482a2d091..415e99eee7fdc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx @@ -21,7 +21,7 @@ import { InstallationCallout } from './installation_callout'; import { UpdateConnector } from './update_connector'; import { updateActionConnector } from '../../../lib/action_connector_api'; import { Credentials } from './credentials'; -import { checkConnectorIsDeprecated } from '../../../../common/connectors_dropdown'; +import { checkConnectorIsDeprecated } from '../../../../common/connectors_selection'; // eslint-disable-next-line import/no-default-export export { ServiceNowConnectorFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itsm_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itsm_params.tsx index aec0aac3a2950..0f94010f18453 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itsm_params.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itsm_params.tsx @@ -26,7 +26,7 @@ import { useGetChoices } from './use_get_choices'; import { choicesToEuiOptions, DEFAULT_CORRELATION_ID } from './helpers'; import * as i18n from './translations'; -import { checkConnectorIsDeprecated } from '../../../../common/connectors_dropdown'; +import { checkConnectorIsDeprecated } from '../../../../common/connectors_selection'; const useGetChoicesFields = ['urgency', 'severity', 'impact', 'category', 'subcategory']; const defaultFields: Fields = { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.test.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.test.tsx rename to x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.test.tsx index ef182736ba4df..212ff9c898f0e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.test.tsx @@ -11,7 +11,7 @@ import { mountWithIntl } from '@kbn/test/jest'; import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common'; import { ActionConnector } from '../../../../types'; -import ServiceNowSelectableRowComponent from './servicenow_dropdown_row'; +import ServiceNowSelectableRowComponent from './servicenow_selection_row'; const connector: ActionConnector = { secrets: {}, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.tsx similarity index 92% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.tsx rename to x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.tsx index 38f823fcbdcb6..b68f4be2e9459 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_dropdown_row.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.tsx @@ -9,12 +9,13 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; +import { ActionConnector } from '../../../../types'; import { + deprecatedMessage, + preconfiguredMessage, connectorDeprecatedMessage, checkConnectorIsDeprecated, -} from '../../../../common/connectors_dropdown'; -import { ActionConnector } from '../../../../types'; -import { deprecatedMessage, preconfiguredMessage } from '../../../../common/connectors_dropdown'; +} from '../../../../common/connectors_selection'; // eslint-disable-next-line import/no-default-export export { ServiceNowSelectableRowComponent as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx index f8308b612f448..2a4988faeeb36 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx @@ -27,7 +27,7 @@ import { useGetChoices } from './use_get_choices'; import { ServiceNowSIRActionParams, Fields, Choice } from './types'; import { choicesToEuiOptions, DEFAULT_CORRELATION_ID } from './helpers'; import { DeprecatedCallout } from './deprecated_callout'; -import { checkConnectorIsDeprecated } from '../../../../common/connectors_dropdown'; +import { checkConnectorIsDeprecated } from '../../../../common/connectors_selection'; const useGetChoicesFields = ['category', 'subcategory', 'priority']; const defaultFields: Fields = { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx index a11397eefe414..a9a507ac7793e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx @@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n'; import { ActionConnector, ActionTypeIndex, ActionTypeModel, AlertAction } from '../../../types'; import { getValidConnectors } from '../common/connectors'; -import { preconfiguredMessage } from '../../../common/connectors_dropdown'; +import { preconfiguredMessage } from '../../../common/connectors_selection'; interface DropdownProps { actionItem: AlertAction; 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 97b64ebfecbde..7f6514297b5fb 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 @@ -51,7 +51,7 @@ import { connectorDeprecatedMessage, deprecatedMessage, checkConnectorIsDeprecated, -} from '../../../../common/connectors_dropdown'; +} from '../../../../common/connectors_selection'; const ConnectorIconTipWithSpacing = withTheme(({ theme }: { theme: EuiTheme }) => { return ( diff --git a/x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.test.tsx b/x-pack/plugins/triggers_actions_ui/public/common/connectors_seleciton.test.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.test.tsx rename to x-pack/plugins/triggers_actions_ui/public/common/connectors_seleciton.test.tsx index 9f1c9e4fcc60b..152dc0eeb8871 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/common/connectors_seleciton.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { checkConnectorIsDeprecated } from './connectors_dropdown'; +import { checkConnectorIsDeprecated } from './connectors_selection'; describe('Connectors select', () => { describe('isDeprecatedConnector', () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.tsx b/x-pack/plugins/triggers_actions_ui/public/common/connectors_selection.tsx similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/common/connectors_dropdown.tsx rename to x-pack/plugins/triggers_actions_ui/public/common/connectors_selection.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index ede403334abf8..d6193fc5a8294 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -135,7 +135,7 @@ export interface ActionTypeModel > | null; actionParamsFields: React.LazyExoticComponent>>; - actionConnectorDropdownComponent?: React.LazyExoticComponent< + customConnectorSelectItemComponent?: React.LazyExoticComponent< ComponentType<{ actionConnector: ActionConnector }> >; } From 1c706b8deaae32f79234d297c084fd816604c1e1 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Mon, 22 Nov 2021 11:49:54 -0500 Subject: [PATCH 12/18] Renaming connector dropdown to selection --- .../sections/action_connector_form/action_type_form.tsx | 4 ++-- ...nector_dropdown.test.tsx => connectors_selection.test.tsx} | 4 ++-- .../{connector_dropdown.tsx => connectors_selection.tsx} | 4 ++-- .../public/common/connectors_seleciton.test.tsx | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) rename x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/{connector_dropdown.test.tsx => connectors_selection.test.tsx} (97%) rename x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/{connector_dropdown.tsx => connectors_selection.tsx} (96%) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx index dd22bfd401e5a..8719e1afea9f5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx @@ -42,7 +42,7 @@ import { ActionAccordionFormProps, ActionGroupWithMessageVariables } from './act import { transformActionVariables } from '../../lib/action_variables'; import { useKibana } from '../../../common/lib/kibana'; import { DefaultActionParams } from '../../lib/get_defaults_for_action_params'; -import { ConnectorsDropdown } from './connector_dropdown'; +import { ConnectorsSelection } from './connectors_selection'; export type ActionTypeFormProps = { actionItem: AlertAction; @@ -245,7 +245,7 @@ export const ActionTypeForm = ({ ) : null } > - { +describe('connectors_selection', () => { const mockedActionParamsFields = React.lazy(async () => ({ default() { return ( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx rename to x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.tsx index a9a507ac7793e..d90ba93ae7d0c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_dropdown.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.tsx @@ -22,9 +22,9 @@ interface DropdownProps { onConnectorSelected: (id: string) => void; } -export const ConnectorsDropdown = React.memo(ConnectorsDropdownComponent); +export const ConnectorsSelection = React.memo(ConnectorsSelectionComponent); -function ConnectorsDropdownComponent({ +function ConnectorsSelectionComponent({ actionItem, accordionIndex, actionTypesIndex, diff --git a/x-pack/plugins/triggers_actions_ui/public/common/connectors_seleciton.test.tsx b/x-pack/plugins/triggers_actions_ui/public/common/connectors_seleciton.test.tsx index 152dc0eeb8871..8e55b71699d65 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/connectors_seleciton.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/common/connectors_seleciton.test.tsx @@ -8,7 +8,7 @@ import { checkConnectorIsDeprecated } from './connectors_selection'; describe('Connectors select', () => { - describe('isDeprecatedConnector', () => { + describe('checkConnectorIsDeprecated', () => { const connector = { id: 'test', actionTypeId: '.webhook', From fa0f045c66cf7da815b5fa543f5d5fa9e20c4e87 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Mon, 22 Nov 2021 11:58:06 -0500 Subject: [PATCH 13/18] Fixing type errors --- .../action_connector_form/connectors_selection.test.tsx | 6 +++--- .../action_connector_form/connectors_selection.tsx | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.test.tsx index 4db7f151ec699..e057d4f20a2a1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.test.tsx @@ -9,7 +9,7 @@ import * as React from 'react'; import { render, screen } from '@testing-library/react'; import { mountWithIntl } from '@kbn/test/jest'; import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common'; -import { ConnectorsDropdown } from './connectors_selection'; +import { ConnectorsSelection } from './connectors_selection'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { ActionType, ConnectorValidationResult, GenericValidationResult } from '../../../types'; import { EuiFieldText } from '@elastic/eui'; @@ -91,7 +91,7 @@ describe('connectors_selection', () => { it('renders a super selector', () => { const wrapper = mountWithIntl( - { it('renders the title of the connector', () => { render( - getValidConnectors(connectors, actionItem, actionTypesIndex), [actionItem, actionTypesIndex, connectors] @@ -82,8 +82,8 @@ const createConnectorOptions = ( const title = getTitle(connector); const ConnectorRow = () => - actionTypeRegistered.actionConnectorDropdownComponent != null ? ( - + actionTypeRegistered.customConnectorSelectItemComponent != null ? ( + ) : ( {title} From 3f02f8e78cccc5807d1f1768ec55eac04dc90f13 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Mon, 22 Nov 2021 12:32:18 -0500 Subject: [PATCH 14/18] Fixing type error --- .../sections/action_connector_form/connector_add_inline.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx index 966d5ecb7cab3..92b6e1461d698 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx @@ -28,7 +28,7 @@ import { hasSaveActionsCapability } from '../../lib/capabilities'; import { ActionAccordionFormProps } from './action_form'; import { useKibana } from '../../../common/lib/kibana'; import { getValidConnectors } from '../common/connectors'; -import { ConnectorsDropdown } from './connector_dropdown'; +import { ConnectorsSelection } from './connectors_selection'; type AddConnectorInFormProps = { actionTypesIndex: ActionTypeIndex; @@ -127,7 +127,7 @@ export const AddConnectorInline = ({ error={connectorDropdownErrors} isInvalid > - Date: Mon, 22 Nov 2021 15:03:35 -0500 Subject: [PATCH 15/18] Fixing translation error --- x-pack/plugins/translations/translations/ja-JP.json | 1 - x-pack/plugins/translations/translations/zh-CN.json | 1 - 2 files changed, 2 deletions(-) diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index e9ecfea653249..bf94fe3a1a458 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -26051,7 +26051,6 @@ "xpack.triggersActionsUI.sections.actionsConnectorsList.connectorsListTable.columns.actions.deleteActionDisabledDescription": "コネクターを削除できません", "xpack.triggersActionsUI.sections.actionsConnectorsList.connectorsListTable.columns.actions.deleteActionName": "削除", "xpack.triggersActionsUI.sections.actionsConnectorsList.connectorsListTable.columns.actions.fixActionDescription": "コネクター構成を修正", - "xpack.triggersActionsUI.sections.actionsConnectorsList.connectorsListTable.columns.actions.isDeprecatedDescription": "このコネクターは廃止予定です。更新するか新しく作成してください。", "xpack.triggersActionsUI.sections.actionsConnectorsList.connectorsListTable.columns.actions.missingSecretsDescription": "機密情報はインポートされませんでした", "xpack.triggersActionsUI.sections.actionsConnectorsList.connectorsListTable.columns.actions.runConnectorDescription": "このコネクターを実行", "xpack.triggersActionsUI.sections.actionsConnectorsList.connectorsListTable.columns.actions.runConnectorDisabledDescription": "コネクターを実行できません", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 8d55a8f7aeb3f..68c56fa4e94fb 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -26500,7 +26500,6 @@ "xpack.triggersActionsUI.sections.actionsConnectorsList.connectorsListTable.columns.actions.deleteActionDisabledDescription": "无法删除连接器", "xpack.triggersActionsUI.sections.actionsConnectorsList.connectorsListTable.columns.actions.deleteActionName": "删除", "xpack.triggersActionsUI.sections.actionsConnectorsList.connectorsListTable.columns.actions.fixActionDescription": "修复连接器配置", - "xpack.triggersActionsUI.sections.actionsConnectorsList.connectorsListTable.columns.actions.isDeprecatedDescription": "此连接器已过时。请进行更新,或创建新连接器。", "xpack.triggersActionsUI.sections.actionsConnectorsList.connectorsListTable.columns.actions.missingSecretsDescription": "未导入敏感信息", "xpack.triggersActionsUI.sections.actionsConnectorsList.connectorsListTable.columns.actions.runConnectorDescription": "运行此连接器", "xpack.triggersActionsUI.sections.actionsConnectorsList.connectorsListTable.columns.actions.runConnectorDisabledDescription": "无法运行连接器", From b30886778c967121fc5b7bf0910d0c26774eb0ac Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Tue, 23 Nov 2021 10:46:10 -0500 Subject: [PATCH 16/18] Fixing test --- .../servicenow/servicenow_selection_row.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.test.tsx index 212ff9c898f0e..b7fb7dd68fe3e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.test.tsx @@ -19,7 +19,7 @@ const connector: ActionConnector = { usesTableApi: true, }, id: 'test', - actionTypeId: '.test', + actionTypeId: '.servicenow', name: 'Test', isPreconfigured: false, }; From 0ed891f0075a513d2ab6c266ee0d328f44fca2ab Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Thu, 2 Dec 2021 17:22:51 -0500 Subject: [PATCH 17/18] Addressing ux feedback and using ComboBox --- x-pack/plugins/triggers_actions_ui/README.md | 16 ++- .../servicenow/helpers.test.ts | 41 +++++- .../servicenow/helpers.ts | 25 +++- .../servicenow/servicenow.tsx | 11 +- .../servicenow_selection_row.test.tsx | 71 --------- .../servicenow/servicenow_selection_row.tsx | 60 ++------ .../action_form.test.tsx | 45 +++--- .../action_type_form.tsx | 136 ++++++++---------- .../connector_add_inline.tsx | 70 +++++---- .../connectors_selection.test.tsx | 6 +- .../connectors_selection.tsx | 110 ++++++++------ .../triggers_actions_ui/public/types.ts | 9 +- 12 files changed, 287 insertions(+), 313 deletions(-) delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.test.tsx diff --git a/x-pack/plugins/triggers_actions_ui/README.md b/x-pack/plugins/triggers_actions_ui/README.md index c5b0daf76e28f..9a834e304bccc 100644 --- a/x-pack/plugins/triggers_actions_ui/README.md +++ b/x-pack/plugins/triggers_actions_ui/README.md @@ -1117,8 +1117,11 @@ Each action type should be defined as an `ActionTypeModel` object with the follo validateParams: (actionParams: any) => Promise; actionConnectorFields: React.FunctionComponent | null; actionParamsFields: React.LazyExoticComponent>>; - customConnectorSelectItemComponent?: React. - LazyExoticComponent + customConnectorSelectItem?: { + getText: (connector: ActionConnector) => string; + getComponent: (connector: ActionConnector) => React. + LazyExoticComponent | undefined; + }; ``` |Property|Description| |---|---| @@ -1129,7 +1132,14 @@ Each action type should be defined as an `ActionTypeModel` object with the follo |validateParams|Validation function for action params.| |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.| -|customConnectorSelectItemComponent|Optional, a lazy loaded React component for customizing the selection row of the action connector form.| +|customConnectorSelectItem|Optional, an object for customizing the selection row of the action connector form.| + +### customConnectorSelectItem Property + +|Property|Description| +|---|---| +|getText|Function for returning the text to display for the row.| +|getComponent|Function for returning a lazy loaded React component for customizing the selection row of the action connector form. Or undefined if if no customization is needed.| ## Register action type model diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.test.ts index e40db85bcb12d..912b308d8d79c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.test.ts @@ -5,7 +5,26 @@ * 2.0. */ -import { isRESTApiError, isFieldInvalid } from './helpers'; +import { + isRESTApiError, + isFieldInvalid, + getConnectorDescriptiveTitle, + getSelectedConnectorIcon, +} from './helpers'; +import { ActionConnector } from '../../../../types'; + +const deprecatedConnector: ActionConnector = { + secrets: {}, + config: { + usesTableApi: true, + }, + id: 'test', + actionTypeId: '.servicenow', + name: 'Test', + isPreconfigured: false, +}; + +const validConnector = { ...deprecatedConnector, config: { usesTableApi: false } }; describe('helpers', () => { describe('isRESTApiError', () => { @@ -48,4 +67,24 @@ describe('helpers', () => { expect(isFieldInvalid('description', [])).toBeFalsy(); }); }); + + describe('getConnectorDescriptiveTitle', () => { + it('adds deprecated to the connector name when the connector usesTableApi', () => { + expect(getConnectorDescriptiveTitle(deprecatedConnector)).toEqual('Test (deprecated)'); + }); + + it('does not add deprecated when the connector has usesTableApi:false', () => { + expect(getConnectorDescriptiveTitle(validConnector)).toEqual('Test'); + }); + }); + + describe('getSelectedConnectorIcon', () => { + it('returns undefined when the connector has usesTableApi:false', () => { + expect(getSelectedConnectorIcon(validConnector)).toBeUndefined(); + }); + + it('returns a component when the connector has usesTableApi:true', () => { + expect(getSelectedConnectorIcon(deprecatedConnector)).toBeDefined(); + }); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts index a6230b1c42669..610759a569fd3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts @@ -5,9 +5,14 @@ * 2.0. */ +import { lazy, ComponentType } from 'react'; import { EuiSelectOption } from '@elastic/eui'; -import { IErrorObject } from '../../../../../public/types'; import { AppInfo, Choice, RESTApiError } from './types'; +import { ActionConnector, IErrorObject } from '../../../../types'; +import { + deprecatedMessage, + checkConnectorIsDeprecated, +} from '../../../../common/connectors_selection'; export const DEFAULT_CORRELATION_ID = '{{rule.id}}:{{alert.id}}'; @@ -21,3 +26,21 @@ export const isFieldInvalid = ( field: string | undefined | null, error: string | IErrorObject | string[] ): boolean => error !== undefined && error.length > 0 && field != null; + +export const getConnectorDescriptiveTitle = (connector: ActionConnector) => { + let title = connector.name; + + if (checkConnectorIsDeprecated(connector)) { + title += ` ${deprecatedMessage}`; + } + + return title; +}; + +export const getSelectedConnectorIcon = ( + actionConnector: ActionConnector +): React.LazyExoticComponent> | undefined => { + if (checkConnectorIsDeprecated(actionConnector)) { + return lazy(() => import('./servicenow_selection_row')); + } +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx index eac950c9c58e3..c38afbe5e748f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx @@ -21,6 +21,7 @@ import { ServiceNowITOMActionParams, } from './types'; import { isValidUrl } from '../../../lib/value_validators'; +import { getConnectorDescriptiveTitle, getSelectedConnectorIcon } from './helpers'; const validateConnector = async ( action: ServiceNowActionConnector @@ -138,7 +139,10 @@ export function getServiceNowITSMActionType(): ActionTypeModel< return validationResult; }, actionParamsFields: lazy(() => import('./servicenow_itsm_params')), - customConnectorSelectItemComponent: lazy(() => import('./servicenow_selection_row')), + customConnectorSelectItem: { + getText: getConnectorDescriptiveTitle, + getComponent: getSelectedConnectorIcon, + }, }; } @@ -175,7 +179,10 @@ export function getServiceNowSIRActionType(): ActionTypeModel< return validationResult; }, actionParamsFields: lazy(() => import('./servicenow_sir_params')), - customConnectorSelectItemComponent: lazy(() => import('./servicenow_selection_row')), + customConnectorSelectItem: { + getText: getConnectorDescriptiveTitle, + getComponent: getSelectedConnectorIcon, + }, }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.test.tsx deleted file mode 100644 index b7fb7dd68fe3e..0000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.test.tsx +++ /dev/null @@ -1,71 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { mountWithIntl } from '@kbn/test/jest'; - -import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common'; -import { ActionConnector } from '../../../../types'; -import ServiceNowSelectableRowComponent from './servicenow_selection_row'; - -const connector: ActionConnector = { - secrets: {}, - config: { - usesTableApi: true, - }, - id: 'test', - actionTypeId: '.servicenow', - name: 'Test', - isPreconfigured: false, -}; - -describe('ServiceNowSelectableRowComponent', () => { - it('adds deprecated to the connector title when its config is marked as deprecated', () => { - render( - - - - ); - - expect(screen.queryByText('Test (deprecated)')).toBeInTheDocument(); - }); - - it('renders an icon marking the connector as deprecated when its config is marked as deprecated', () => { - const wrapper = mountWithIntl( - - - - ); - - expect(wrapper.find('[data-test-subj="deprecated-connector-icon-test"]').exists()).toBeTruthy(); - }); - - it('does not add the deprecated text to the connector title when it is not deprecated', () => { - const nonDeprecatedConnector = { ...connector, config: { usesTableApi: false } }; - - render( - - - - ); - - expect(screen.queryByText('Test')).toBeInTheDocument(); - }); - - it('does not render an icon marking the connector as deprecated when its config is marked as not deprecated', () => { - const nonDeprecatedConnector = { ...connector, config: { usesTableApi: false } }; - - const wrapper = mountWithIntl( - - - - ); - - expect(wrapper.find('[data-test-subj="deprecated-connector-icon-test"]').exists()).toBeFalsy(); - }); -}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.tsx index b68f4be2e9459..65068c6f56a07 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_selection_row.tsx @@ -4,69 +4,33 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiFlexItem, EuiIconTip } from '@elastic/eui'; +import { EuiIconTip } from '@elastic/eui'; import React from 'react'; import { i18n } from '@kbn/i18n'; -import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; import { ActionConnector } from '../../../../types'; -import { - deprecatedMessage, - preconfiguredMessage, - connectorDeprecatedMessage, - checkConnectorIsDeprecated, -} from '../../../../common/connectors_selection'; +import { connectorDeprecatedMessage } from '../../../../common/connectors_selection'; // eslint-disable-next-line import/no-default-export -export { ServiceNowSelectableRowComponent as default }; +export { ServiceNowSelectableRowIcon as default }; -function ServiceNowSelectableRowComponent({ +export function ServiceNowSelectableRowIcon({ actionConnector, }: { actionConnector: ActionConnector; }) { - const title = getTitle(actionConnector); - return ( - <> - - {title} - - {checkConnectorIsDeprecated(actionConnector) && ( - - - - )} - + ); } -const getTitle = (connector: ActionConnector) => { - let title = connector.name; - - if (connector.isPreconfigured) { - title += ` ${preconfiguredMessage}`; - } - - if (checkConnectorIsDeprecated(connector)) { - title += ` ${deprecatedMessage}`; - } - - return title; -}; - -const StyledIconTip = euiStyled(EuiIconTip)` - margin-left: ${({ theme }) => theme.eui.euiSizeS} - margin-bottom: 0 !important; -`; - const deprecatedTooltipTitle = i18n.translate( 'xpack.triggersActionsUI.sections.actionForm.deprecatedTooltipTitle', { 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 268db7d6c69af..a26349949446d 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 @@ -497,25 +497,23 @@ describe('action_form', () => { Array [ Object { "data-test-subj": "dropdown-connector-test", - "inputDisplay": - - , - "value": "test", + "key": "test", + "label": "Test connector", + "value": Object { + "id": "test", + "prependComponent": undefined, + "title": "Test connector", + }, }, Object { "data-test-subj": "dropdown-connector-test2", - "inputDisplay": - - , - "value": "test2", + "key": "test2", + "label": "Test connector 2", + "value": Object { + "id": "test2", + "prependComponent": undefined, + "title": "Test connector 2", + }, }, ] `); @@ -530,14 +528,13 @@ describe('action_form', () => { Array [ Object { "data-test-subj": "dropdown-connector-test3", - "inputDisplay": - - , - "value": "test3", + "key": "test3", + "label": "Preconfigured Only", + "value": Object { + "id": "test3", + "prependComponent": undefined, + "title": "Preconfigured Only", + }, }, ] `); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx index c8462f00c866d..10244213614e2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx @@ -20,7 +20,6 @@ import { EuiIconTip, EuiText, EuiFormLabel, - EuiFormControlLayout, EuiSuperSelect, EuiBadge, EuiErrorBoundary, @@ -176,86 +175,73 @@ export const ActionTypeForm = ({ <> {actionGroups && selectedActionGroup && setActionGroupIdByIndex && ( <> - - - - - - } - > - ({ - value, - inputDisplay: actionGroupDisplay(value, name, actionItem.actionTypeId), - disabled: isActionGroupDisabled(value, actionItem.actionTypeId), - 'data-test-subj': `addNewActionConnectorActionGroup-${index}-option-${value}`, - }))} - valueOfSelected={selectedActionGroup.id} - onChange={(group) => { - setActionGroupIdByIndex(group, index); - setActionGroup(group); - }} + + - - - + + } + fullWidth + id={`addNewActionConnectorActionGroup-${actionItem.actionTypeId}`} + data-test-subj={`addNewActionConnectorActionGroup-${index}`} + options={actionGroups.map(({ id: value, name }) => ({ + value, + inputDisplay: actionGroupDisplay(value, name, actionItem.actionTypeId), + disabled: isActionGroupDisabled(value, actionItem.actionTypeId), + 'data-test-subj': `addNewActionConnectorActionGroup-${index}-option-${value}`, + }))} + valueOfSelected={selectedActionGroup.id} + onChange={(group) => { + setActionGroupIdByIndex(group, index); + setActionGroup(group); + }} + /> + )} - - - + } + labelAppend={ + canSave && + actionTypesIndex && + actionTypesIndex[actionConnector.actionTypeId].enabledInConfig ? ( + - } - labelAppend={ - canSave && - actionTypesIndex && - actionTypesIndex[actionConnector.actionTypeId].enabledInConfig ? ( - - - - ) : null - } - > - - - - + + ) : null + } + > + + {ParamsFieldsComponent ? ( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx index f4ec83a46ea43..cd274c542c9d5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx @@ -99,45 +99,41 @@ export const AddConnectorInline = ({ }, []); const connectorsDropdown = ( - - - - } - labelAppend={ - - - - } - error={connectorDropdownErrors} - isInvalid + + } + labelAppend={ + - - - - + + } + error={connectorDropdownErrors} + isInvalid + > + + ); return ( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.test.tsx index e057d4f20a2a1..6c383913a3c0f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.test.tsx @@ -88,7 +88,7 @@ describe('connectors_selection', () => { beforeEach(() => {}); - it('renders a super selector', () => { + it('renders a selector', () => { const wrapper = mountWithIntl( { ); - // there should be two, the first is because it is the default selected row, and then another - // as the row option - expect(screen.queryAllByText('test pagerduty')).toHaveLength(2); + expect(screen.queryAllByText('test pagerduty')).toHaveLength(1); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.tsx index e4f4b129cbeb7..0964cd7387e80 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.tsx @@ -5,13 +5,18 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiSuperSelect } from '@elastic/eui'; -import React, { useCallback, useMemo } from 'react'; +import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; +import React, { useCallback, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { ActionConnector, ActionTypeIndex, ActionTypeModel, AlertAction } from '../../../types'; import { getValidConnectors } from '../common/connectors'; -import { preconfiguredMessage } from '../../../common/connectors_selection'; + +interface ConnectorOption { + title: string; + id: string; + prependComponent?: JSX.Element; +} interface SelectionProps { actionItem: AlertAction; @@ -37,9 +42,9 @@ function ConnectorsSelectionComponent({ [actionItem, actionTypesIndex, connectors] ); - const valueOfSelected = useMemo( - () => getValueOfSelectedConnector(actionItem.id, validConnectors), - [actionItem.id, validConnectors] + const selectedConnectors = useMemo( + () => getValueOfSelectedConnector(actionItem.id, validConnectors, actionTypeRegistered), + [actionItem.id, validConnectors, actionTypeRegistered] ); const options = useMemo( @@ -47,68 +52,85 @@ function ConnectorsSelectionComponent({ [validConnectors, actionTypeRegistered] ); - const onChange = useCallback((id: string) => onConnectorSelected(id), [onConnectorSelected]); + const [selectedOption, setSelectedOption] = useState< + EuiComboBoxOptionOption | undefined + >(selectedConnectors.length > 0 ? selectedConnectors[0] : undefined); + + const onChange = useCallback( + (connectorOptions: Array>) => { + setSelectedOption(connectorOptions[0]); + onConnectorSelected(connectorOptions[0].value?.id ?? ''); + }, + [onConnectorSelected] + ); return ( - ); } const getValueOfSelectedConnector = ( actionItemId: string, - connectors: ActionConnector[] -): string | undefined => { + connectors: ActionConnector[], + actionTypeRegistered: ActionTypeModel +): Array> => { const selectedConnector = connectors.find((connector) => connector.id === actionItemId); if (!selectedConnector) { - return; + return []; } - return actionItemId; + return [createOption(selectedConnector, actionTypeRegistered)]; }; const createConnectorOptions = ( connectors: ActionConnector[], actionTypeRegistered: ActionTypeModel -) => - connectors.map((connector) => { - const title = getTitle(connector); - - const ConnectorRow = () => - actionTypeRegistered.customConnectorSelectItemComponent != null ? ( - - ) : ( - - {title} - - ); - - return { - value: connector.id, - inputDisplay: ( - - - - ), - 'data-test-subj': `dropdown-connector-${connector.id}`, - }; - }); - -const getTitle = (connector: ActionConnector) => { - let title = connector.name; - - if (connector.isPreconfigured) { - title += ` ${preconfiguredMessage}`; +): Array> => + connectors.map((connector) => createOption(connector, actionTypeRegistered)); + +const createOption = (connector: ActionConnector, actionTypeRegistered: ActionTypeModel) => { + const title = getTitle(connector, actionTypeRegistered); + + let prependComponent: JSX.Element | undefined; + + if (actionTypeRegistered.customConnectorSelectItem != null) { + const CustomPrependComponent = + actionTypeRegistered.customConnectorSelectItem.getComponent(connector); + if (CustomPrependComponent) { + prependComponent = ; + } + } + + return { + label: title, + value: { + title, + id: connector.id, + prependComponent, + }, + key: connector.id, + 'data-test-subj': `dropdown-connector-${connector.id}`, + }; +}; + +const getTitle = (connector: ActionConnector, actionTypeRegistered: ActionTypeModel) => { + if (actionTypeRegistered.customConnectorSelectItem != null) { + return actionTypeRegistered.customConnectorSelectItem.getText(connector); } - return title; + return connector.name; }; const incidentManagemSystem = i18n.translate( diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index d6193fc5a8294..381db41987bff 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -135,9 +135,12 @@ export interface ActionTypeModel > | null; actionParamsFields: React.LazyExoticComponent>>; - customConnectorSelectItemComponent?: React.LazyExoticComponent< - ComponentType<{ actionConnector: ActionConnector }> - >; + customConnectorSelectItem?: { + getText: (actionConnector: ActionConnector) => string; + getComponent: ( + actionConnector: ActionConnector + ) => React.LazyExoticComponent> | undefined; + }; } export interface GenericValidationResult { From 09145c92cc866a0c8111e98112d8619e6b80bbe2 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Tue, 7 Dec 2021 14:14:10 -0500 Subject: [PATCH 18/18] Extracting customConnectorSelectItem to an interface --- x-pack/plugins/triggers_actions_ui/README.md | 14 ++++++++------ x-pack/plugins/triggers_actions_ui/public/types.ts | 14 ++++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/README.md b/x-pack/plugins/triggers_actions_ui/README.md index 9a834e304bccc..dacd26cdf7eeb 100644 --- a/x-pack/plugins/triggers_actions_ui/README.md +++ b/x-pack/plugins/triggers_actions_ui/README.md @@ -1117,11 +1117,7 @@ Each action type should be defined as an `ActionTypeModel` object with the follo validateParams: (actionParams: any) => Promise; actionConnectorFields: React.FunctionComponent | null; actionParamsFields: React.LazyExoticComponent>>; - customConnectorSelectItem?: { - getText: (connector: ActionConnector) => string; - getComponent: (connector: ActionConnector) => React. - LazyExoticComponent | undefined; - }; + customConnectorSelectItem?: CustomConnectorSelectionItem; ``` |Property|Description| |---|---| @@ -1134,7 +1130,13 @@ Each action type should be defined as an `ActionTypeModel` object with the follo |actionParamsFields|A lazy loaded React component for building UI of current action type params. Displayed as a part of Create Alert flyout.| |customConnectorSelectItem|Optional, an object for customizing the selection row of the action connector form.| -### customConnectorSelectItem Property +### CustomConnectorSelectionItem Properties + +``` + getText: (connector: ActionConnector) => string; + getComponent: (connector: ActionConnector) => React. + LazyExoticComponent | undefined; +``` |Property|Description| |---|---| diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 381db41987bff..0902669c5694c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -118,6 +118,13 @@ export interface Sorting { direction: string; } +interface CustomConnectorSelectionItem { + getText: (actionConnector: ActionConnector) => string; + getComponent: ( + actionConnector: ActionConnector + ) => React.LazyExoticComponent> | undefined; +} + export interface ActionTypeModel { id: string; iconClass: IconType; @@ -135,12 +142,7 @@ export interface ActionTypeModel > | null; actionParamsFields: React.LazyExoticComponent>>; - customConnectorSelectItem?: { - getText: (actionConnector: ActionConnector) => string; - getComponent: ( - actionConnector: ActionConnector - ) => React.LazyExoticComponent> | undefined; - }; + customConnectorSelectItem?: CustomConnectorSelectionItem; } export interface GenericValidationResult {