Skip to content

Commit

Permalink
[Cases] Adding deprecated icon to additional actions dropdown selecto…
Browse files Browse the repository at this point in the history
…rs (#115287)

* adding deprecated icon to the other actions list

* Adding deprecated text to list view

* Each action type can render the dropdown row

* Refactoring and fixing todos

* Fixing jest tests

* Adding and fixing other tests

* Fixing functional test

* Fixing tests

* Adjusting text to match cases

* Fixing tests

* Addressing pr feedback

* Renaming connector dropdown to selection

* Fixing type errors

* Fixing type error

* Fixing translation error

* Fixing test

* Addressing ux feedback and using ComboBox

* Extracting customConnectorSelectItem to an interface

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
jonathan-buttner and kibanamachine authored Dec 8, 2021
1 parent 6c81068 commit e94d63f
Show file tree
Hide file tree
Showing 22 changed files with 751 additions and 307 deletions.
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -25956,7 +25956,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": "コネクターを実行できません",
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -26404,7 +26404,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": "无法运行连接器",
Expand Down
15 changes: 15 additions & 0 deletions x-pack/plugins/triggers_actions_ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1117,6 +1117,7 @@ Each action type should be defined as an `ActionTypeModel` object with the follo
validateParams: (actionParams: any) => Promise<ValidationResult>;
actionConnectorFields: React.FunctionComponent<any> | null;
actionParamsFields: React.LazyExoticComponent<ComponentType<ActionParamsProps<ActionParams>>>;
customConnectorSelectItem?: CustomConnectorSelectionItem;
```
|Property|Description|
|---|---|
Expand All @@ -1127,6 +1128,20 @@ 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.|
|customConnectorSelectItem|Optional, an object for customizing the selection row of the action connector form.|

### CustomConnectorSelectionItem Properties

```
getText: (connector: ActionConnector) => string;
getComponent: (connector: ActionConnector) => React.
LazyExoticComponent<ComponentType<{ actionConnector: ActionConnector }> | undefined;
```

|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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,21 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';

interface Props {
onMigrate: () => void;
onMigrate?: () => void;
}

const DeprecatedCalloutComponent: React.FC<Props> = ({ onMigrate }) => {
const update =
onMigrate != null ? (
<EuiLink onClick={onMigrate} data-test-subj="update-connector-btn">
{updateThisConnectorMessage}
</EuiLink>
) : (
<span>{updateThisConnectorMessage}</span>
);

return (
<>
<EuiSpacer size="s" />
<EuiCallOut
size="m"
iconType="alert"
Expand All @@ -34,16 +42,7 @@ const DeprecatedCalloutComponent: React.FC<Props> = ({ onMigrate }) => {
defaultMessage="{update} {create} "
id="xpack.triggersActionsUI.components.builtinActionTypes.servicenow.appInstallationInfo"
values={{
update: (
<EuiLink onClick={onMigrate} data-test-subj="update-connector-btn">
{i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutMigrate',
{
defaultMessage: 'Update this connector,',
}
)}
</EuiLink>
),
update,
create: (
<span>
{i18n.translate(
Expand All @@ -63,3 +62,10 @@ const DeprecatedCalloutComponent: React.FC<Props> = ({ onMigrate }) => {
};

export const DeprecatedCallout = memo(DeprecatedCalloutComponent);

const updateThisConnectorMessage = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.deprecatedCalloutMigrate',
{
defaultMessage: 'Update this connector,',
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,26 @@
* 2.0.
*/

import { isRESTApiError, isFieldInvalid, isDeprecatedConnector } 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', () => {
Expand Down Expand Up @@ -49,50 +68,23 @@ describe('helpers', () => {
});
});

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);
describe('getConnectorDescriptiveTitle', () => {
it('adds deprecated to the connector name when the connector usesTableApi', () => {
expect(getConnectorDescriptiveTitle(deprecatedConnector)).toEqual('Test (deprecated)');
});

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('does not add deprecated when the connector has usesTableApi:false', () => {
expect(getConnectorDescriptiveTitle(validConnector)).toEqual('Test');
});
});

it('returns true if the connector is .servicenow and the usesTableApi=true', () => {
expect(
isDeprecatedConnector({
...connector,
actionTypeId: '.servicenow',
config: { ...connector.config, usesTableApi: true },
})
).toBe(true);
describe('getSelectedConnectorIcon', () => {
it('returns undefined when the connector has usesTableApi:false', () => {
expect(getSelectedConnectorIcon(validConnector)).toBeUndefined();
});

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);
it('returns a component when the connector has usesTableApi:true', () => {
expect(getSelectedConnectorIcon(deprecatedConnector)).toBeDefined();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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, ServiceNowActionConnector } from './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}}';

Expand All @@ -22,22 +27,20 @@ export const isFieldInvalid = (
error: string | IErrorObject | string[]
): boolean => error !== undefined && error.length > 0 && field != null;

// TODO: Remove when the applications are certified
export const isDeprecatedConnector = (connector?: ServiceNowActionConnector): boolean => {
if (connector == null) {
return false;
}
export const getConnectorDescriptiveTitle = (connector: ActionConnector) => {
let title = connector.name;

if (connector.actionTypeId === '.servicenow' || connector.actionTypeId === '.servicenow-sir') {
/**
* Connector's prior to the Elastic ServiceNow application
* use the Table API (https://developer.servicenow.com/dev.do#!/reference/api/rome/rest/c_TableAPI)
* Connectors after the Elastic ServiceNow application use the
* Import Set API (https://developer.servicenow.com/dev.do#!/reference/api/rome/rest/c_ImportSetAPI)
* A ServiceNow connector is considered deprecated if it uses the Table API.
*/
return !!connector.config.usesTableApi;
if (checkConnectorIsDeprecated(connector)) {
title += ` ${deprecatedMessage}`;
}

return false;
return title;
};

export const getSelectedConnectorIcon = (
actionConnector: ActionConnector
): React.LazyExoticComponent<ComponentType<{ actionConnector: ActionConnector }>> | undefined => {
if (checkConnectorIsDeprecated(actionConnector)) {
return lazy(() => import('./servicenow_selection_row'));
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
ServiceNowITOMActionParams,
} from './types';
import { isValidUrl } from '../../../lib/value_validators';
import { getConnectorDescriptiveTitle, getSelectedConnectorIcon } from './helpers';

const validateConnector = async (
action: ServiceNowActionConnector
Expand Down Expand Up @@ -138,6 +139,10 @@ export function getServiceNowITSMActionType(): ActionTypeModel<
return validationResult;
},
actionParamsFields: lazy(() => import('./servicenow_itsm_params')),
customConnectorSelectItem: {
getText: getConnectorDescriptiveTitle,
getComponent: getSelectedConnectorIcon,
},
};
}

Expand Down Expand Up @@ -174,6 +179,10 @@ export function getServiceNowSIRActionType(): ActionTypeModel<
return validationResult;
},
actionParamsFields: lazy(() => import('./servicenow_sir_params')),
customConnectorSelectItem: {
getText: getConnectorDescriptiveTitle,
getComponent: getSelectedConnectorIcon,
},
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -15,11 +16,16 @@ 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, isDeprecatedConnector } 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 { checkConnectorIsDeprecated } from '../../../../common/connectors_selection';

// eslint-disable-next-line import/no-default-export
export { ServiceNowConnectorFields as default };

// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { snExternalServiceConfig } from '../../../../../../actions/server/builtin_action_types/servicenow/config';

Expand All @@ -40,7 +46,7 @@ const ServiceNowConnectorFields: React.FC<ActionConnectorFieldsProps<ServiceNowA
} = useKibana().services;
const { apiUrl, usesTableApi } = action.config;
const { username, password } = action.secrets;
const requiresNewApplication = !isDeprecatedConnector(action);
const requiresNewApplication = !checkConnectorIsDeprecated(action);

const [showUpdateConnector, setShowUpdateConnector] = useState(false);

Expand Down Expand Up @@ -156,7 +162,7 @@ const ServiceNowConnectorFields: React.FC<ActionConnectorFieldsProps<ServiceNowA
{requiresNewApplication && (
<InstallationCallout appId={snExternalServiceConfig[action.actionTypeId].appId ?? ''} />
)}
{!requiresNewApplication && <DeprecatedCallout onMigrate={onMigrateClick} />}
{!requiresNewApplication && <SpacedDeprecatedCallout onMigrate={onMigrateClick} />}
<Credentials
action={action}
errors={errors}
Expand All @@ -175,5 +181,9 @@ const ServiceNowConnectorFields: React.FC<ActionConnectorFieldsProps<ServiceNowA
);
};

// eslint-disable-next-line import/no-default-export
export { ServiceNowConnectorFields as default };
const SpacedDeprecatedCallout = ({ onMigrate }: { onMigrate: () => void }) => (
<>
<EuiSpacer size="s" />
<DeprecatedCallout onMigrate={onMigrate} />
</>
);
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ import { FormattedMessage } from '@kbn/i18n-react';

import { useKibana } from '../../../../common/lib/kibana';
import { ActionParamsProps } from '../../../../types';
import { ServiceNowITSMActionParams, Choice, Fields, ServiceNowActionConnector } from './types';
import { ServiceNowITSMActionParams, Choice, Fields } from './types';
import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables';
import { TextFieldWithMessageVariables } from '../../text_field_with_message_variables';
import { useGetChoices } from './use_get_choices';
import { choicesToEuiOptions, DEFAULT_CORRELATION_ID, isDeprecatedConnector } from './helpers';
import { choicesToEuiOptions, DEFAULT_CORRELATION_ID } from './helpers';

import * as i18n from './translations';
import { checkConnectorIsDeprecated } from '../../../../common/connectors_selection';

const useGetChoicesFields = ['urgency', 'severity', 'impact', 'category', 'subcategory'];
const defaultFields: Fields = {
Expand All @@ -46,6 +47,8 @@ const ServiceNowParamsFields: React.FunctionComponent<
notifications: { toasts },
} = useKibana().services;

const isDeprecatedActionConnector = checkConnectorIsDeprecated(actionConnector);

const actionConnectorRef = useRef(actionConnector?.id ?? '');
const { incident, comments } = useMemo(
() =>
Expand Down Expand Up @@ -238,7 +241,7 @@ const ServiceNowParamsFields: React.FunctionComponent<
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="m" />
{!isDeprecatedConnector(actionConnector as unknown as ServiceNowActionConnector) && (
{!isDeprecatedActionConnector && (
<>
<EuiFlexGroup>
<EuiFlexItem>
Expand Down
Loading

0 comments on commit e94d63f

Please sign in to comment.