From 2884d903de43c5f846a96385215b303397826973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Georgiana-Andreea=20Onolea=C8=9B=C4=83?= Date: Wed, 4 Dec 2024 16:05:38 +0200 Subject: [PATCH] [ResponseOps][Alerting] Show missing Slack connectors in the new rule form (#202315) Closes https://github.com/elastic/kibana/issues/201673 ## Summary - changed the logic to create the connectorsMap (which gives us the connector type filters): - Only the '**slack'** and **'slack API'** connector types include a `subtype` array. I updated the logic so that when the `actionTypeModel` has **hideInUi** set to true (indicating a 'slack API' connector), it searches for a **'slack'** connector in the subtype. If a **'slack'** connector is found, `otherActionTypeId` is set to 'slack'; otherwise, it is set to `undefined`. This effectively "maps" the 'slack API' type to the 'slack' type within the `connectorsMap` - changed the logic to create the `filteredConnectors` (which gives us the connectors to display, filtered after the type): - The **selectedConnectorType** can only be '**slack'** because, in the `connectorsMap`, the '**slack API'** option has already been mapped to '**slack'** and is no longer included as a separate type. - If the `subtype` includes **'slack',** the filter will return connectors with `actionTypeId` of either 'slack' or 'slack API'. - This ensures that both 'slack' and 'slack API' connectors are displayed, as 'slack API' is associated with the 'slack' type through the subtype. https://github.com/user-attachments/assets/9cccaf42-b6db-4eea-b2fd-8f37f0e24745 ## Release note Fix Slack API connectors not displayed under Slack connector type when adding new connector to rule. --- .../rule_actions_connectors_modal.test.tsx | 86 +++++++++++++++++++ .../rule_actions_connectors_modal.tsx | 37 +++++--- 2 files changed, 111 insertions(+), 12 deletions(-) diff --git a/packages/response-ops/rule_form/src/rule_actions/rule_actions_connectors_modal.test.tsx b/packages/response-ops/rule_form/src/rule_actions/rule_actions_connectors_modal.test.tsx index aaf2ff0658f5d..d8c183820d3cb 100644 --- a/packages/response-ops/rule_form/src/rule_actions/rule_actions_connectors_modal.test.tsx +++ b/packages/response-ops/rule_form/src/rule_actions/rule_actions_connectors_modal.test.tsx @@ -195,6 +195,92 @@ describe('ruleActionsConnectorsModal', () => { expect(screen.queryByText('connector2')).not.toBeInTheDocument(); }); + test('should not render connector filter if hideInUi is true', async () => { + const actionTypeRegistry = new TypeRegistry(); + actionTypeRegistry.register( + getActionTypeModel('1', { + id: 'actionType-1', + subtype: [ + { id: 'actionType-1', name: 'connector-1' }, + { id: 'actionType-2', name: 'connector-2' }, + ], + }) + ); + actionTypeRegistry.register( + getActionTypeModel('2', { + id: 'actionType-2', + hideInUi: true, + subtype: [ + { id: 'actionType-1', name: 'connector-1' }, + { id: 'actionType-2', name: 'connector-2' }, + ], + }) + ); + useRuleFormState.mockReturnValue({ + plugins: { + actionTypeRegistry, + }, + formData: { + actions: [], + }, + connectors: mockConnectors, + connectorTypes: mockActionTypes, + }); + + render( + + ); + const filterButtonGroup = screen.getByTestId('ruleActionsConnectorsModalFilterButtonGroup'); + expect(within(filterButtonGroup).getByText('actionType: 1')).toBeInTheDocument(); + expect(within(filterButtonGroup).queryByText('actionType: 2')).not.toBeInTheDocument(); + expect(within(filterButtonGroup).getByText('All')).toBeInTheDocument(); + + expect(screen.getAllByTestId('ruleActionsConnectorsModalFilterButton').length).toEqual(2); + }); + + test('should display connectors if hideInUi is true and it has subtype', async () => { + const actionTypeRegistry = new TypeRegistry(); + actionTypeRegistry.register( + getActionTypeModel('1', { + id: 'actionType-1', + subtype: [ + { id: 'actionType-1', name: 'connector-1' }, + { id: 'actionType-2', name: 'connector-2' }, + ], + }) + ); + actionTypeRegistry.register( + getActionTypeModel('2', { + id: 'actionType-2', + hideInUi: true, + subtype: [ + { id: 'actionType-1', name: 'connector-1' }, + { id: 'actionType-2', name: 'connector-2' }, + ], + }) + ); + useRuleFormState.mockReturnValue({ + plugins: { + actionTypeRegistry, + }, + formData: { + actions: [], + }, + connectors: mockConnectors, + connectorTypes: mockActionTypes, + }); + + render( + + ); + const filterButtonGroup = screen.getByTestId('ruleActionsConnectorsModalFilterButtonGroup'); + + await userEvent.click(within(filterButtonGroup).getByText('actionType: 1')); + expect(screen.getAllByTestId('ruleActionsConnectorsModalCard').length).toEqual(2); + expect(screen.getByText('connector-1')).toBeInTheDocument(); + expect(screen.getByText('connector-2')).toBeInTheDocument(); + }); + test('should not render connector if actionsParamsField doesnt exist', () => { const actionTypeRegistry = new TypeRegistry(); actionTypeRegistry.register(getActionTypeModel('1', { id: 'actionType-1' })); diff --git a/packages/response-ops/rule_form/src/rule_actions/rule_actions_connectors_modal.tsx b/packages/response-ops/rule_form/src/rule_actions/rule_actions_connectors_modal.tsx index 64db54845530a..d411e468a8a83 100644 --- a/packages/response-ops/rule_form/src/rule_actions/rule_actions_connectors_modal.tsx +++ b/packages/response-ops/rule_form/src/rule_actions/rule_actions_connectors_modal.tsx @@ -77,9 +77,7 @@ export const RuleActionsConnectorsModal = (props: RuleActionsConnectorsModalProp if (!actionType) { return false; } - if (actionTypeModel.hideInUi) { - return false; - } + if (!actionTypeModel.actionParamsFields) { return false; } @@ -92,6 +90,7 @@ export const RuleActionsConnectorsModal = (props: RuleActionsConnectorsModalProp if (!actionType.enabledInConfig && !checkEnabledResult.isEnabled) { return false; } + return true; }); }, [connectors, connectorTypes, preconfiguredConnectors, actionTypeRegistry]); @@ -119,29 +118,43 @@ export const RuleActionsConnectorsModal = (props: RuleActionsConnectorsModalProp const connectorsMap: ConnectorsMap | null = useMemo(() => { return availableConnectors.reduce((result, { actionTypeId }) => { - if (result[actionTypeId]) { - result[actionTypeId].total += 1; + const actionTypeModel = actionTypeRegistry.get(actionTypeId); + const subtype = actionTypeModel.subtype; + + const shownActionTypeId = actionTypeModel.hideInUi + ? subtype?.filter((type) => type.id !== actionTypeId)[0].id + : undefined; + + const currentActionTypeId = shownActionTypeId ? shownActionTypeId : actionTypeId; + + if (result[currentActionTypeId]) { + result[currentActionTypeId].total += 1; } else { - result[actionTypeId] = { - actionTypeId, + result[currentActionTypeId] = { + actionTypeId: currentActionTypeId, total: 1, - name: connectorTypes.find(({ id }) => actionTypeId === id)?.name || '', + name: connectorTypes.find(({ id }) => id === currentActionTypeId)?.name || '', }; } + return result; }, {}); - }, [availableConnectors, connectorTypes]); + }, [availableConnectors, connectorTypes, actionTypeRegistry]); const filteredConnectors = useMemo(() => { return availableConnectors .filter(({ actionTypeId }) => { + const subtype = actionTypeRegistry.get(actionTypeId).subtype?.map((type) => type.id); + if (selectedConnectorType === 'all' || selectedConnectorType === '') { return true; } - if (selectedConnectorType === actionTypeId) { - return true; + + if (subtype?.includes(selectedConnectorType)) { + return subtype.includes(actionTypeId); } - return false; + + return selectedConnectorType === actionTypeId; }) .filter(({ actionTypeId, name }) => { const trimmedSearchValue = searchValue.trim().toLocaleLowerCase();