Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Cases] Improve connectors mapping #101145

Merged
merged 6 commits into from
Jun 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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