Skip to content

Commit

Permalink
[SIEM][Case] Update connector through flyout (elastic#60307)
Browse files Browse the repository at this point in the history
* Move add flyout to parent

* Disable mapping

* Show edit flyout

* Do not update connectors throught cases API

* Fix uncontrolled input error

* Disable edit button

* Add comments

* Change undefined to null

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
cnasikas and elasticmachine committed Mar 18, 2020
1 parent 7f93862 commit 27c4a45
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 216 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { KibanaServices } from '../../../lib/kibana';
import { CASES_CONFIGURE_URL } from '../constants';
import { ApiProps } from '../types';
import { convertToCamelCase, decodeCaseConfigureResponse } from '../utils';
import { CaseConfigure, PatchConnectorProps } from './types';
import { CaseConfigure } from './types';

export const fetchConnectors = async ({ signal }: ApiProps): Promise<CasesConnectorsFindResult> => {
const response = await KibanaServices.get().http.fetch(
Expand Down Expand Up @@ -79,20 +79,3 @@ export const patchCaseConfigure = async (
decodeCaseConfigureResponse(response)
);
};

export const patchConfigConnector = async ({
connectorId,
config,
signal,
}: PatchConnectorProps): Promise<CasesConnectorsFindResult> => {
const response = await KibanaServices.get().http.fetch(
`${CASES_CONFIGURE_URL}/connectors/${connectorId}`,
{
method: 'PATCH',
body: JSON.stringify(config),
signal,
}
);

return response;
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { ElasticUser, ApiProps } from '../types';
import { ElasticUser } from '../types';
import {
ActionType,
CasesConnectorConfiguration,
CasesConfigurationMaps,
CaseField,
ClosureType,
Expand All @@ -33,11 +32,6 @@ export interface CaseConfigure {
version: string;
}

export interface PatchConnectorProps extends ApiProps {
connectorId: string;
config: CasesConnectorConfiguration;
}

export interface CCMapsCombinedActionAttributes extends CasesConfigurationMaps {
actionType?: ActionType;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ import { useState, useEffect, useCallback } from 'react';

import { useStateToaster, errorToToaster } from '../../../components/toasters';
import * as i18n from '../translations';
import { fetchConnectors, patchConfigConnector } from './api';
import { CasesConfigurationMapping, Connector } from './types';
import { fetchConnectors } from './api';
import { Connector } from './types';

export interface ReturnConnectors {
loading: boolean;
connectors: Connector[];
refetchConnectors: () => void;
updateConnector: (connectorId: string, mappings: CasesConfigurationMapping[]) => unknown;
}

export const useConnectors = (): ReturnConnectors => {
Expand Down Expand Up @@ -53,55 +52,6 @@ export const useConnectors = (): ReturnConnectors => {
};
}, []);

const updateConnector = useCallback(
(connectorId: string, mappings: CasesConfigurationMapping[]) => {
if (connectorId === 'none') {
return;
}

let didCancel = false;
const abortCtrl = new AbortController();
const update = async () => {
try {
setLoading(true);
await patchConfigConnector({
connectorId,
config: {
cases_configuration: {
mapping: mappings.map(m => ({
source: m.source,
target: m.target,
action_type: m.actionType,
})),
},
},
signal: abortCtrl.signal,
});
if (!didCancel) {
setLoading(false);
refetchConnectors();
}
} catch (error) {
if (!didCancel) {
setLoading(false);
refetchConnectors();
errorToToaster({
title: i18n.ERROR_TITLE,
error: error.body && error.body.message ? new Error(error.body.message) : error,
dispatchToaster,
});
}
}
};
update();
return () => {
didCancel = true;
abortCtrl.abort();
};
},
[]
);

useEffect(() => {
refetchConnectors();
}, []);
Expand All @@ -110,6 +60,5 @@ export const useConnectors = (): ReturnConnectors => {
loading,
connectors,
refetchConnectors,
updateConnector,
};
};
10 changes: 7 additions & 3 deletions x-pack/legacy/plugins/siem/public/lib/connectors/servicenow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ export function getActionType(): ActionTypeModel {
const ServiceNowConnectorFields: React.FunctionComponent<ActionConnectorFieldsProps<
ServiceNowActionConnector
>> = ({ action, editActionConfig, editActionSecrets, errors }) => {
/* We do not provide defaults values to the fields (like empty string for apiUrl) intentionally.
* If we do, errors will be shown the first time the flyout is open even though the user did not
* interact with the form. Also, we would like to show errors for empty fields provided by the user.
/*/
const { apiUrl, casesConfiguration: { mapping = [] } = {} } = action.config;
const { username, password } = action.secrets;

Expand Down Expand Up @@ -153,7 +157,7 @@ const ServiceNowConnectorFields: React.FunctionComponent<ActionConnectorFieldsPr
fullWidth
isInvalid={isApiUrlInvalid}
name="apiUrl"
value={apiUrl}
value={apiUrl || ''} // Needed to prevent uncontrolled input error when value is undefined
data-test-subj="apiUrlFromInput"
placeholder="https://<instance>.service-now.com"
onChange={handleOnChangeActionConfig.bind(null, 'apiUrl')}
Expand All @@ -175,7 +179,7 @@ const ServiceNowConnectorFields: React.FunctionComponent<ActionConnectorFieldsPr
fullWidth
isInvalid={isUsernameInvalid}
name="username"
value={username}
value={username || ''} // Needed to prevent uncontrolled input error when value is undefined
data-test-subj="usernameFromInput"
onChange={handleOnChangeSecretConfig.bind(null, 'username')}
onBlur={handleOnBlurSecretConfig.bind(null, 'username')}
Expand All @@ -196,7 +200,7 @@ const ServiceNowConnectorFields: React.FunctionComponent<ActionConnectorFieldsPr
fullWidth
isInvalid={isPasswordInvalid}
name="password"
value={password}
value={password || ''} // Needed to prevent uncontrolled input error when value is undefined
data-test-subj="passwordFromInput"
onChange={handleOnChangeSecretConfig.bind(null, 'password')}
onBlur={handleOnBlurSecretConfig.bind(null, 'password')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React, { useState, useCallback } from 'react';
import React from 'react';
import {
EuiDescribedFormGroup,
EuiFormRow,
Expand All @@ -18,12 +18,7 @@ import styled from 'styled-components';
import { ConnectorsDropdown } from './connectors_dropdown';
import * as i18n from './translations';

import {
ActionsConnectorsContextProvider,
ConnectorAddFlyout,
} from '../../../../../../../../plugins/triggers_actions_ui/public';
import { Connector } from '../../../../containers/case/configure/types';
import { useKibana } from '../../../../lib/kibana';

const EuiFormRowExtended = styled(EuiFormRow)`
.euiFormRow__labelWrapper {
Expand All @@ -38,41 +33,26 @@ interface Props {
disabled: boolean;
isLoading: boolean;
onChangeConnector: (id: string) => void;
refetchConnectors: () => void;
selectedConnector: string;
handleShowAddFlyout: () => void;
}
const actionTypes = [
{
id: '.servicenow',
name: 'ServiceNow',
enabled: true,
},
];

const ConnectorsComponent: React.FC<Props> = ({
connectors,
disabled,
isLoading,
onChangeConnector,
refetchConnectors,
selectedConnector,
handleShowAddFlyout,
}) => {
const { http, triggers_actions_ui, notifications, application } = useKibana().services;
const [addFlyoutVisible, setAddFlyoutVisibility] = useState<boolean>(false);

const handleShowFlyout = useCallback(() => setAddFlyoutVisibility(true), []);

const dropDownLabel = (
<EuiFlexGroup justifyContent="spaceBetween">
<EuiFlexItem grow={false}>{i18n.INCIDENT_MANAGEMENT_SYSTEM_LABEL}</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiLink onClick={handleShowFlyout}>{i18n.ADD_NEW_CONNECTOR}</EuiLink>
<EuiLink onClick={handleShowAddFlyout}>{i18n.ADD_NEW_CONNECTOR}</EuiLink>
</EuiFlexItem>
</EuiFlexGroup>
);

const reloadConnectors = useCallback(async () => refetchConnectors(), []);

return (
<>
<EuiDescribedFormGroup
Expand All @@ -90,21 +70,6 @@ const ConnectorsComponent: React.FC<Props> = ({
/>
</EuiFormRowExtended>
</EuiDescribedFormGroup>
<ActionsConnectorsContextProvider
value={{
http,
actionTypeRegistry: triggers_actions_ui.actionTypeRegistry,
toastNotifications: notifications.toasts,
capabilities: application.capabilities,
reloadConnectors,
}}
>
<ConnectorAddFlyout
addFlyoutVisible={addFlyoutVisible}
setAddFlyoutVisibility={setAddFlyoutVisibility}
actionTypes={actionTypes}
/>
</ActionsConnectorsContextProvider>
</>
);
};
Expand Down
Loading

0 comments on commit 27c4a45

Please sign in to comment.