Skip to content

Commit

Permalink
[Cases] Improve connectors mapping (#101145) (#101862)
Browse files Browse the repository at this point in the history
  • Loading branch information
cnasikas authored Jun 10, 2021
1 parent 96a5d4f commit e47e1dc
Show file tree
Hide file tree
Showing 39 changed files with 516 additions and 1,121 deletions.
2 changes: 2 additions & 0 deletions x-pack/plugins/cases/common/api/connectors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export enum ConnectorTypes {
none = '.none',
}

export const connectorTypes = Object.values(ConnectorTypes);

const ConnectorJiraTypeFieldsRt = rt.type({
type: rt.literal(ConnectorTypes.jira),
fields: rt.union([JiraFieldsRT, rt.null]),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ describe('Mapping', () => {
wrappingComponent: TestProviders,
});
expect(wrapper.find('[data-test-subj="field-mapping-desc"]').first().text()).toBe(
'Field mappings require an established connection to ServiceNow ITSM. Please check your connection credentials.'
'Failed to retrieve mappings for ServiceNow ITSM.'
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,7 @@ export const FIELD_MAPPING_DESC = (thirdPartyName: string): string => {
export const FIELD_MAPPING_DESC_ERR = (thirdPartyName: string): string => {
return i18n.translate('xpack.cases.configureCases.fieldMappingDescErr', {
values: { thirdPartyName },
defaultMessage:
'Field mappings require an established connection to { thirdPartyName }. Please check your connection credentials.',
defaultMessage: 'Failed to retrieve mappings for { thirdPartyName }.',
});
};
export const EDIT_FIELD_MAPPING_TITLE = (thirdPartyName: string): string => {
Expand Down
5 changes: 3 additions & 2 deletions x-pack/plugins/cases/server/client/cases/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { createCaseError, flattenCaseSavedObject, getAlertInfoFromComments } fro
import { ENABLE_CASE_CONNECTOR } from '../../../common/constants';
import { CasesClient, CasesClientArgs, CasesClientInternal } from '..';
import { Operations } from '../../authorization';
import { casesConnectors } from '../../connectors';

/**
* Returns true if the case should be closed based on the configuration settings and whether the case
Expand Down Expand Up @@ -110,8 +111,7 @@ export const push = async (
});

const connectorMappings = await casesClientInternal.configuration.getMappings({
connectorId: connector.id,
connectorType: connector.actionTypeId,
connector: theCase.connector,
});

if (connectorMappings.length === 0) {
Expand All @@ -125,6 +125,7 @@ export const push = async (
connector: connector as ActionConnector,
mappings: connectorMappings[0].attributes.mappings,
alerts,
casesConnectors,
});

const pushRes = await actionsClient.execute({
Expand Down
9 changes: 9 additions & 0 deletions x-pack/plugins/cases/server/client/cases/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
} from './utils';
import { flattenCaseSavedObject } from '../../common';
import { SECURITY_SOLUTION_OWNER } from '../../../common';
import { casesConnectors } from '../../connectors';

const formatComment = {
commentId: commentObj.id,
Expand Down Expand Up @@ -443,6 +444,7 @@ describe('utils', () => {
connector,
mappings,
alerts: [],
casesConnectors,
});

expect(res).toEqual({
Expand Down Expand Up @@ -471,6 +473,7 @@ describe('utils', () => {
connector,
mappings,
alerts: [],
casesConnectors,
});

expect(res.comments).toEqual([
Expand Down Expand Up @@ -501,6 +504,7 @@ describe('utils', () => {
},
],
alerts: [],
casesConnectors,
});

expect(res.comments).toEqual([]);
Expand Down Expand Up @@ -531,6 +535,7 @@ describe('utils', () => {
},
],
alerts: [],
casesConnectors,
});

expect(res.comments).toEqual([
Expand Down Expand Up @@ -561,6 +566,7 @@ describe('utils', () => {
connector,
mappings,
alerts: [],
casesConnectors,
});

expect(res.comments).toEqual([
Expand Down Expand Up @@ -595,6 +601,7 @@ describe('utils', () => {
connector,
mappings,
alerts: [],
casesConnectors,
});

expect(res).toEqual({
Expand Down Expand Up @@ -626,6 +633,7 @@ describe('utils', () => {
connector,
mappings,
alerts: [],
casesConnectors,
}).catch((e) => {
expect(e).not.toBeNull();
expect(e).toEqual(
Expand All @@ -645,6 +653,7 @@ describe('utils', () => {
connector: { ...connector, actionTypeId: 'not-supported' },
mappings,
alerts: [],
casesConnectors,
}).catch((e) => {
expect(e).not.toBeNull();
expect(e).toEqual(new Error('Invalid external service'));
Expand Down
25 changes: 9 additions & 16 deletions x-pack/plugins/cases/server/client/cases/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,15 @@ import {
CaseFullExternalService,
CaseResponse,
CaseUserActionsResponse,
CommentAttributes,
CommentRequestAlertType,
CommentRequestUserType,
CommentResponse,
CommentResponseAlertsType,
CommentType,
ConnectorMappingsAttributes,
ConnectorTypes,
CommentAttributes,
CommentRequestUserType,
CommentRequestAlertType,
} from '../../../common';
import { ActionsClient } from '../../../../actions/server';
import { externalServiceFormatters, FormatterConnectorTypes } from '../../connectors';
import { CasesClientGetAlertsResponse } from '../../client/alerts/types';
import {
BasicParams,
Expand All @@ -39,6 +37,7 @@ import {
TransformFieldsArgs,
} from './types';
import { getAlertIds } from '../utils';
import { CasesConnectorsMap } from '../../connectors';

interface CreateIncidentArgs {
actionsClient: ActionsClient;
Expand All @@ -47,6 +46,7 @@ interface CreateIncidentArgs {
connector: ActionConnector;
mappings: ConnectorMappingsAttributes[];
alerts: CasesClientGetAlertsResponse;
casesConnectors: CasesConnectorsMap;
}

export const getLatestPushInfo = (
Expand All @@ -70,9 +70,6 @@ export const getLatestPushInfo = (
return null;
};

const isConnectorSupported = (connectorId: string): connectorId is FormatterConnectorTypes =>
Object.values(ConnectorTypes).includes(connectorId as ConnectorTypes);

const getCommentContent = (comment: CommentResponse): string => {
if (comment.type === CommentType.user) {
return comment.comment;
Expand All @@ -99,6 +96,7 @@ export const createIncident = async ({
connector,
mappings,
alerts,
casesConnectors,
}: CreateIncidentArgs): Promise<MapIncident> => {
const {
comments: caseComments,
Expand All @@ -110,20 +108,15 @@ export const createIncident = async ({
updated_by: updatedBy,
} = theCase;

if (!isConnectorSupported(connector.actionTypeId)) {
throw new Error('Invalid external service');
}

const params = { title, description, createdAt, createdBy, updatedAt, updatedBy };
const latestPushInfo = getLatestPushInfo(connector.id, userActions);
const externalId = latestPushInfo?.pushedInfo?.external_id ?? null;
const defaultPipes = externalId ? ['informationUpdated'] : ['informationCreated'];
let currentIncident: ExternalServiceParams | undefined;

const externalServiceFields = externalServiceFormatters[connector.actionTypeId].format(
theCase,
alerts
);
const externalServiceFields =
casesConnectors.get(connector.actionTypeId)?.format(theCase, alerts) ?? {};

let incident: Partial<PushToServiceApiParams['incident']> = { ...externalServiceFields };

if (externalId) {
Expand Down
41 changes: 14 additions & 27 deletions x-pack/plugins/cases/server/client/configure/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import {
excess,
GetConfigureFindRequest,
GetConfigureFindRequestRt,
GetFieldsResponse,
throwErrors,
CasesConfigurationsResponse,
CaseConfigurationsResponseRt,
Expand All @@ -41,20 +40,14 @@ import {
} from '../../common';
import { CasesClientInternal } from '../client_internal';
import { CasesClientArgs } from '../types';
import { getFields } from './get_fields';
import { getMappings } from './get_mappings';

// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { FindActionResult } from '../../../../actions/server/types';
import { ActionType } from '../../../../actions/common';
import { Operations } from '../../authorization';
import { combineAuthorizedAndOwnerFilter } from '../utils';
import {
ConfigurationGetFields,
MappingsArgs,
CreateMappingsArgs,
UpdateMappingsArgs,
} from './types';
import { MappingsArgs, CreateMappingsArgs, UpdateMappingsArgs } from './types';
import { createMappings } from './create_mappings';
import { updateMappings } from './update_mappings';
import {
Expand All @@ -69,7 +62,6 @@ import {
* @ignore
*/
export interface InternalConfigureSubClient {
getFields(params: ConfigurationGetFields): Promise<GetFieldsResponse>;
getMappings(
params: MappingsArgs
): Promise<SavedObjectsFindResponse<ConnectorMappings>['saved_objects']>;
Expand Down Expand Up @@ -116,12 +108,9 @@ export const createInternalConfigurationSubClient = (
casesClientInternal: CasesClientInternal
): InternalConfigureSubClient => {
const configureSubClient: InternalConfigureSubClient = {
getFields: (params: ConfigurationGetFields) => getFields(params, clientArgs),
getMappings: (params: MappingsArgs) => getMappings(params, clientArgs),
createMappings: (params: CreateMappingsArgs) =>
createMappings(params, clientArgs, casesClientInternal),
updateMappings: (params: UpdateMappingsArgs) =>
updateMappings(params, clientArgs, casesClientInternal),
createMappings: (params: CreateMappingsArgs) => createMappings(params, clientArgs),
updateMappings: (params: UpdateMappingsArgs) => updateMappings(params, clientArgs),
};

return Object.freeze(configureSubClient);
Expand Down Expand Up @@ -194,8 +183,7 @@ async function get(
if (connector != null) {
try {
mappings = await casesClientInternal.configuration.getMappings({
connectorId: connector.id,
connectorType: connector.type,
connector: transformESConnectorToCaseConnector(connector),
});
} catch (e) {
error = e.isBoom
Expand Down Expand Up @@ -303,32 +291,32 @@ async function update(

try {
const resMappings = await casesClientInternal.configuration.getMappings({
connectorId: connector != null ? connector.id : configuration.attributes.connector.id,
connectorType: connector != null ? connector.type : configuration.attributes.connector.type,
connector:
connector != null
? connector
: transformESConnectorToCaseConnector(configuration.attributes.connector),
});
mappings = resMappings.length > 0 ? resMappings[0].attributes.mappings : [];

if (connector != null) {
if (resMappings.length !== 0) {
mappings = await casesClientInternal.configuration.updateMappings({
connectorId: connector.id,
connectorType: connector.type,
connector,
mappingId: resMappings[0].id,
});
} else {
mappings = await casesClientInternal.configuration.createMappings({
connectorId: connector.id,
connectorType: connector.type,
connector,
owner: configuration.attributes.owner,
});
}
}
} catch (e) {
error = e.isBoom
? e.output.payload.message
: `Error connecting to ${
: `Error creating mapping for ${
connector != null ? connector.name : configuration.attributes.connector.name
} instance`;
}`;
}

const patch = await caseConfigureService.patch({
Expand Down Expand Up @@ -429,14 +417,13 @@ async function create(

try {
mappings = await casesClientInternal.configuration.createMappings({
connectorId: configuration.connector.id,
connectorType: configuration.connector.type,
connector: configuration.connector,
owner: configuration.owner,
});
} catch (e) {
error = e.isBoom
? e.output.payload.message
: `Error connecting to ${configuration.connector.name} instance`;
: `Error creating mapping for ${configuration.connector.name}`;
}

const post = await caseConfigureService.post({
Expand Down
25 changes: 9 additions & 16 deletions x-pack/plugins/cases/server/client/configure/create_mappings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,48 +5,41 @@
* 2.0.
*/

import { ConnectorMappingsAttributes, ConnectorTypes } from '../../../common/api';
import { ConnectorMappingsAttributes } from '../../../common/api';
import { ACTION_SAVED_OBJECT_TYPE } from '../../../../actions/server';
import { createCaseError } from '../../common/error';
import { CasesClientArgs, CasesClientInternal } from '..';
import { CasesClientArgs } from '..';
import { CreateMappingsArgs } from './types';
import { casesConnectors } from '../../connectors';

export const createMappings = async (
{ connectorType, connectorId, owner }: CreateMappingsArgs,
clientArgs: CasesClientArgs,
casesClientInternal: CasesClientInternal
{ connector, owner }: CreateMappingsArgs,
clientArgs: CasesClientArgs
): Promise<ConnectorMappingsAttributes[]> => {
const { unsecuredSavedObjectsClient, connectorMappingsService, logger } = clientArgs;

try {
if (connectorType === ConnectorTypes.none) {
return [];
}

const res = await casesClientInternal.configuration.getFields({
connectorId,
connectorType,
});
const mappings = casesConnectors.get(connector.type)?.getMapping() ?? [];

const theMapping = await connectorMappingsService.post({
unsecuredSavedObjectsClient,
attributes: {
mappings: res.defaultMappings,
mappings,
owner,
},
references: [
{
type: ACTION_SAVED_OBJECT_TYPE,
name: `associated-${ACTION_SAVED_OBJECT_TYPE}`,
id: connectorId,
id: connector.id,
},
],
});

return theMapping.attributes.mappings;
} catch (error) {
throw createCaseError({
message: `Failed to create mapping connector id: ${connectorId} type: ${connectorType}: ${error}`,
message: `Failed to create mapping connector id: ${connector.id} type: ${connector.type}: ${error}`,
error,
logger,
});
Expand Down
Loading

0 comments on commit e47e1dc

Please sign in to comment.