Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const pushToServiceHandler = async ({
}

const fields = prepareFieldsForTransformation({
params,
externalCase: params.externalCase,
mapping,
defaultPipes,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export const ExecutorSubActionSchema = schema.oneOf([
]);

export const ExecutorSubActionPushParamsSchema = schema.object({
caseId: schema.string(),
savedObjectId: schema.string(),
title: schema.string(),
description: schema.nullable(schema.string()),
comments: schema.nullable(schema.arrayOf(CommentSchema)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export interface PipedField {
}

export interface PrepareFieldsForTransformArgs {
params: PushToServiceApiParams;
externalCase: Record<string, any>;
mapping: Map<string, MapRecord>;
defaultPipes?: string[];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const maliciousMapping: MapRecord[] = [
];

const fullParams: PushToServiceApiParams = {
caseId: 'd4387ac5-0899-4dc2-bbfa-0dd605c934aa',
savedObjectId: 'd4387ac5-0899-4dc2-bbfa-0dd605c934aa',
title: 'a title',
description: 'a description',
createdAt: '2020-03-13T08:34:53.450Z',
Expand Down Expand Up @@ -132,7 +132,7 @@ describe('buildMap', () => {
describe('mapParams', () => {
test('maps params correctly', () => {
const params = {
caseId: '123',
savedObjectId: '123',
incidentId: '456',
title: 'Incident title',
description: 'Incident description',
Expand All @@ -148,7 +148,7 @@ describe('mapParams', () => {

test('do not add fields not in mapping', () => {
const params = {
caseId: '123',
savedObjectId: '123',
incidentId: '456',
title: 'Incident title',
description: 'Incident description',
Expand All @@ -164,7 +164,7 @@ describe('mapParams', () => {
describe('prepareFieldsForTransformation', () => {
test('prepare fields with defaults', () => {
const res = prepareFieldsForTransformation({
params: fullParams,
externalCase: fullParams.externalCase,
mapping: finalMapping,
});
expect(res).toEqual([
Expand All @@ -185,7 +185,7 @@ describe('prepareFieldsForTransformation', () => {

test('prepare fields with default pipes', () => {
const res = prepareFieldsForTransformation({
params: fullParams,
externalCase: fullParams.externalCase,
mapping: finalMapping,
defaultPipes: ['myTestPipe'],
});
Expand All @@ -209,7 +209,7 @@ describe('prepareFieldsForTransformation', () => {
describe('transformFields', () => {
test('transform fields for creation correctly', () => {
const fields = prepareFieldsForTransformation({
params: fullParams,
externalCase: fullParams.externalCase,
mapping: finalMapping,
});

Expand All @@ -226,8 +226,8 @@ describe('transformFields', () => {

test('transform fields for update correctly', () => {
const fields = prepareFieldsForTransformation({
params: {
...fullParams,
externalCase: {
...fullParams.externalCase,
updatedAt: '2020-03-15T08:34:53.450Z',
updatedBy: {
username: 'anotherUser',
Expand Down Expand Up @@ -262,7 +262,7 @@ describe('transformFields', () => {

test('add newline character to descripton', () => {
const fields = prepareFieldsForTransformation({
params: fullParams,
externalCase: fullParams.externalCase,
mapping: finalMapping,
defaultPipes: ['informationUpdated'],
});
Expand All @@ -280,7 +280,7 @@ describe('transformFields', () => {

test('append username if fullname is undefined when create', () => {
const fields = prepareFieldsForTransformation({
params: fullParams,
externalCase: fullParams.externalCase,
mapping: finalMapping,
});

Expand All @@ -300,8 +300,8 @@ describe('transformFields', () => {

test('append username if fullname is undefined when update', () => {
const fields = prepareFieldsForTransformation({
params: {
...fullParams,
externalCase: {
...fullParams.externalCase,
updatedAt: '2020-03-15T08:34:53.450Z',
updatedBy: {
username: 'anotherUser',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,17 +182,17 @@ export const addTimeZoneToDate = (date: string, timezone = 'GMT'): string => {
};

export const prepareFieldsForTransformation = ({
params,
externalCase,
mapping,
defaultPipes = ['informationCreated'],
}: PrepareFieldsForTransformArgs): PipedField[] => {
return Object.keys(params.externalCase)
return Object.keys(externalCase)
.filter((p) => mapping.get(p)?.actionType != null && mapping.get(p)?.actionType !== 'nothing')
.map((p) => {
const actionType = mapping.get(p)?.actionType ?? 'nothing';
return {
key: p,
value: params.externalCase[p],
value: externalCase[p],
actionType,
pipes: actionType === 'append' ? [...defaultPipes, 'append'] : defaultPipes,
};
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/actions/server/builtin_action_types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { getActionType as getSlackActionType } from './slack';
import { getActionType as getWebhookActionType } from './webhook';
import { getActionType as getServiceNowActionType } from './servicenow';
import { getActionType as getJiraActionType } from './jira';
import { getActionType as getResilientActionType } from './resilient';

export function registerBuiltInActionTypes({
actionsConfigUtils: configurationUtilities,
Expand All @@ -34,4 +35,5 @@ export function registerBuiltInActionTypes({
actionTypeRegistry.register(getWebhookActionType({ logger, configurationUtilities }));
actionTypeRegistry.register(getServiceNowActionType({ configurationUtilities }));
actionTypeRegistry.register(getJiraActionType({ configurationUtilities }));
actionTypeRegistry.register(getResilientActionType({ logger, configurationUtilities }));
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ mapping.set('summary', {
});

const executorParams: ExecutorSubActionPushParams = {
caseId: 'd4387ac5-0899-4dc2-bbfa-0dd605c934aa',
savedObjectId: 'd4387ac5-0899-4dc2-bbfa-0dd605c934aa',
externalId: 'incident-3',
createdAt: '2020-04-27T10:59:46.202Z',
createdBy: { fullName: 'Elastic User', username: 'elastic' },
Expand Down
140 changes: 140 additions & 0 deletions x-pack/plugins/actions/server/builtin_action_types/resilient/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { flow } from 'lodash';
import {
ExternalServiceParams,
PushToServiceApiHandlerArgs,
HandshakeApiHandlerArgs,
GetIncidentApiHandlerArgs,
ExternalServiceApi,
} from './types';

// TODO: to remove, need to support Case
import { transformers, Transformer } from '../case/transformers';
import { PushToServiceResponse, TransformFieldsArgs } from './case_types';
import { prepareFieldsForTransformation } from '../case/utils';

const handshakeHandler = async ({
externalService,
mapping,
params,
}: HandshakeApiHandlerArgs) => {};
const getIncidentHandler = async ({
externalService,
mapping,
params,
}: GetIncidentApiHandlerArgs) => {};

const pushToServiceHandler = async ({
externalService,
mapping,
params,
secrets,
}: PushToServiceApiHandlerArgs): Promise<PushToServiceResponse> => {
const { externalId, comments } = params;
const updateIncident = externalId ? true : false;
const defaultPipes = updateIncident ? ['informationUpdated'] : ['informationCreated'];
let currentIncident: ExternalServiceParams | undefined;
let res: PushToServiceResponse;

if (externalId) {
currentIncident = await externalService.getIncident(externalId);
}

let incident = {};
// TODO: should be removed later but currently keep it for the Case implementation support
if (mapping) {
const fields = prepareFieldsForTransformation({
externalCase: params.externalObject,
mapping,
defaultPipes,
});

incident = transformFields({
params,
fields,
currentIncident,
});
} else {
incident = { ...params, name: params.title };
}

if (updateIncident) {
res = await externalService.updateIncident({
incidentId: externalId,
incident,
});
} else {
res = await externalService.createIncident({
incident: {
...incident,
caller_id: secrets.username,
},
});
}

// TODO: should temporary keep comments for a Case usage
if (
comments &&
Array.isArray(comments) &&
comments.length > 0 &&
mapping &&
mapping.get('comments')?.actionType !== 'nothing'
) {
res.comments = [];

const fieldsKey = mapping.get('comments')?.target ?? 'comments';
for (const currentComment of comments) {
await externalService.updateIncident({
incidentId: res.id,
incident: {
...incident,
[fieldsKey]: currentComment.comment,
},
});
res.comments = [
...(res.comments ?? []),
{
commentId: currentComment.commentId,
pushedDate: res.pushedDate,
},
];
}
}
return res;
};

export const transformFields = ({
params,
fields,
currentIncident,
}: TransformFieldsArgs): Record<string, string> => {
return fields.reduce((prev, cur) => {
const transform = flow<Transformer>(...cur.pipes.map((p) => transformers[p]));
return {
...prev,
[cur.key]: transform({
value: cur.value,
date: params.updatedAt ?? params.createdAt,
user:
(params.updatedBy != null
? params.updatedBy.fullName
? params.updatedBy.fullName
: params.updatedBy.username
: params.createdBy.fullName
? params.createdBy.fullName
: params.createdBy.username) ?? '',
previousValue: currentIncident ? currentIncident[cur.key] : '',
}).value,
};
}, {});
};

export const api: ExternalServiceApi = {
handshake: handshakeHandler,
pushToService: pushToServiceHandler,
getIncident: getIncidentHandler,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { schema } from '@kbn/config-schema';

export const MappingActionType = schema.oneOf([
schema.literal('nothing'),
schema.literal('overwrite'),
schema.literal('append'),
]);

export const MapRecordSchema = schema.object({
source: schema.string(),
target: schema.string(),
actionType: MappingActionType,
});

export const IncidentConfigurationSchema = schema.object({
mapping: schema.arrayOf(MapRecordSchema),
});

export const EntityInformation = {
createdAt: schema.maybe(schema.string()),
createdBy: schema.maybe(schema.any()),
updatedAt: schema.nullable(schema.string()),
updatedBy: schema.nullable(schema.any()),
};

export const CommentSchema = schema.object({
commentId: schema.string(),
comment: schema.string(),
...EntityInformation,
});
Loading