"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getConnectors = void 0; var _lodash = require("lodash"); var _api = require("../../../common/types/api"); var _api2 = require("../../../common/api"); var _user_actions = require("../../../common/utils/user_actions"); var _error = require("../../common/error"); var _authorization = require("../../authorization"); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ const getConnectors = async ({ caseId }, clientArgs) => { const { services: { userActionService }, logger, authorization, actionsClient } = clientArgs; try { const [connectors, latestUserAction] = await Promise.all([userActionService.getCaseConnectorInformation(caseId), userActionService.getMostRecentUserAction(caseId)]); await checkConnectorsAuthorization({ authorization, connectors, latestUserAction }); const res = await getConnectorsInfo({ caseId, actionsClient, connectors, latestUserAction, userActionService, logger }); return (0, _api2.decodeOrThrow)(_api.GetCaseConnectorsResponseRt)(res); } catch (error) { throw (0, _error.createCaseError)({ message: `Failed to retrieve the case connectors case id: ${caseId}: ${error}`, error, logger }); } }; exports.getConnectors = getConnectors; const checkConnectorsAuthorization = async ({ connectors, latestUserAction, authorization }) => { const entities = latestUserAction ? [{ owner: latestUserAction.attributes.owner, id: latestUserAction.id }] : []; for (const connector of connectors) { entities.push({ owner: connector.fields.attributes.owner, id: connector.connectorId }); if (connector.push) { entities.push(...[{ owner: connector.push.mostRecent.attributes.owner, id: connector.connectorId }, { owner: connector.push.oldest.attributes.owner, id: connector.connectorId }]); } } await authorization.ensureAuthorized({ entities, operation: _authorization.Operations.getConnectors }); }; const getConnectorsInfo = async ({ caseId, connectors, latestUserAction, actionsClient, userActionService, logger }) => { const connectorIds = connectors.map(connector => connector.connectorId); const [pushInfo, actionConnectors] = await Promise.all([getEnrichedPushInfo({ caseId, activity: connectors, userActionService }), await getActionConnectors(actionsClient, logger, connectorIds)]); return createConnectorInfoResult({ actionConnectors, connectors, pushInfo, latestUserAction }); }; const getActionConnectors = async (actionsClient, logger, ids) => { try { return await actionsClient.getBulk({ ids }); } catch (error) { // silent error and log it logger.error(`Failed to retrieve action connectors in the get case connectors route: ${error}`); return []; } }; const getEnrichedPushInfo = async ({ caseId, activity, userActionService }) => { const pushDetails = getPushDetails(activity); const connectorFieldsForPushes = await userActionService.getConnectorFieldsBeforeLatestPush(caseId, pushDetails.map(push => ({ connectorId: push.connectorId, date: push.mostRecentPush }))); const enrichedPushInfo = new Map(); for (const pushInfo of pushDetails) { const connectorFieldsSO = connectorFieldsForPushes.get(pushInfo.connectorId); const connectorFields = getConnectorInfoFromSavedObject(connectorFieldsSO); if (connectorFields != null) { enrichedPushInfo.set(pushInfo.connectorId, { latestPushDate: pushInfo.mostRecentPush, oldestPushDate: pushInfo.oldestPush, externalService: pushInfo.externalService, connectorFieldsUsedInPush: connectorFields }); } } return enrichedPushInfo; }; const getPushDetails = activity => { const pushDetails = []; for (const connectorInfo of activity) { var _connectorInfo$push, _connectorInfo$push2, _connectorInfo$push3; const externalService = getExternalServiceFromSavedObject((_connectorInfo$push = connectorInfo.push) === null || _connectorInfo$push === void 0 ? void 0 : _connectorInfo$push.mostRecent); const mostRecentPushCreatedAt = getDate((_connectorInfo$push2 = connectorInfo.push) === null || _connectorInfo$push2 === void 0 ? void 0 : _connectorInfo$push2.mostRecent.attributes.created_at); const oldestPushCreatedAt = getDate((_connectorInfo$push3 = connectorInfo.push) === null || _connectorInfo$push3 === void 0 ? void 0 : _connectorInfo$push3.oldest.attributes.created_at); if (connectorInfo.push != null && externalService != null && mostRecentPushCreatedAt != null && oldestPushCreatedAt != null) { pushDetails.push({ connectorId: connectorInfo.connectorId, externalService, mostRecentPush: mostRecentPushCreatedAt, oldestPush: oldestPushCreatedAt }); } } return pushDetails; }; const getExternalServiceFromSavedObject = savedObject => { if (savedObject != null && (0, _user_actions.isPushedUserAction)(savedObject.attributes)) { return savedObject.attributes.payload.externalService; } }; const getDate = timestamp => { if (timestamp == null) { return; } const date = new Date(timestamp); if (isDateValid(date)) { return date; } }; const isDateValid = date => { return !isNaN(date.getTime()); }; const getConnectorInfoFromSavedObject = savedObject => { if (savedObject != null && ((0, _user_actions.isConnectorUserAction)(savedObject.attributes) || (0, _user_actions.isCreateCaseUserAction)(savedObject.attributes))) { return savedObject.attributes.payload.connector; } }; const createConnectorInfoResult = ({ actionConnectors, connectors, pushInfo, latestUserAction }) => { const results = {}; const actionConnectorsMap = new Map(actionConnectors.map(actionConnector => [actionConnector.id, { ...actionConnector }])); for (const aggregationConnector of connectors) { const connectorDetails = actionConnectorsMap.get(aggregationConnector.connectorId); const connector = getConnectorInfoFromSavedObject(aggregationConnector.fields); const latestUserActionCreatedAt = getDate(latestUserAction === null || latestUserAction === void 0 ? void 0 : latestUserAction.attributes.created_at); if (connector != null) { var _connectorDetails$nam; const enrichedPushInfo = pushInfo.get(aggregationConnector.connectorId); const needsToBePushed = hasDataToPush({ connector, pushInfo: enrichedPushInfo, latestUserActionDate: latestUserActionCreatedAt }); const pushDetails = convertEnrichedPushInfoToDetails(enrichedPushInfo); results[connector.id] = { ...connector, name: (_connectorDetails$nam = connectorDetails === null || connectorDetails === void 0 ? void 0 : connectorDetails.name) !== null && _connectorDetails$nam !== void 0 ? _connectorDetails$nam : connector.name, push: { needsToBePushed, hasBeenPushed: hasBeenPushed(enrichedPushInfo), ...(pushDetails && { details: pushDetails }) } }; } } return results; }; /** * The algorithm to determine if a specific connector within a case needs to be pushed is as follows: * 1. Check to see if the connector has been used to push, if it hasn't then we need to push * 2. Check to see if the most recent connector fields are different than the connector fields used in the most recent push, * if they are different then we need to push * 3. Check to see if the most recent valid user action (a valid user action is one that changes the title, description, * tags, or creation of a comment) was created after the most recent push (aka did the user do something new that needs * to be pushed) */ const hasDataToPush = ({ connector, pushInfo, latestUserActionDate }) => { return ( /** * This isEqual call satisfies the first two steps of the algorithm above because if a push never occurred then the * push fields will be undefined which will not equal the latest connector fields anyway. */ !(0, _lodash.isEqual)(connector, pushInfo === null || pushInfo === void 0 ? void 0 : pushInfo.connectorFieldsUsedInPush) || pushInfo != null && latestUserActionDate != null && latestUserActionDate > pushInfo.latestPushDate ); }; const hasBeenPushed = pushInfo => { return pushInfo != null; }; const convertEnrichedPushInfoToDetails = info => { if (info == null) { return; } return { latestUserActionPushDate: info.latestPushDate.toISOString(), oldestUserActionPushDate: info.oldestPushDate.toISOString(), externalService: info.externalService }; };